代理模式与它在源码中的运用

栏目: Java · 发布时间: 7年前

内容简介:代理是指一个包装了真实访问对象的类,以便控制对真实类的访问访问流程如下此时RealSubject作为代理对象的一个属性字段,在运行之前就会生成RealSubject的字节码文件,这种方式也称作静态代理

代理是指一个包装了真实访问对象的类,以便控制对真实类的访问

代理模式与它在源码中的运用

访问流程如下

代理模式与它在源码中的运用
public interface SubjectInterface {
    void hi();
}


public class RealSubject implements SubjectInterface {
    @Override
    public void hi() {
        System.out.print("hi");
    }
}

public class SubjectProxy implements SubjectInterface{
    private RealSubject r;
    public SubjectProxy() {
        r=new RealSubject();
    }

    @Override
    public void hi() {
        System.out.println("proxy");
        r.hi();
    }
}

public class Client {
    public static void main(String[] args) {
       SubjectInterface subject = new SubjectProxy();
        subject.hi();
    }
}
复制代码

此时RealSubject作为代理对象的一个属性字段,在运行之前就会生成RealSubject的字节码文件,这种方式也称作静态代理

动态代理

被代理的类在运行时动态生成的,编译的时候并没有生成RealSubject

使用JDK实现动态代理

jdk实现动态代理必须有实现接口InvocationHandler的处理类,用于执行被代理类的方法

public class SubjectInvocationHandler implements InvocationHandler {
    private Object myproxy;

    public SubjectInvocationHandler(Object proxy) {
        this.myproxy = proxy;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("invocation handler");
        method.invoke(myproxy,args);
        return null;
    }
}

复制代码

客户端通过使用 java.lang.reflect.Proxy 自行创建代理,然后调用目标方法即可

public class Client {
    public static void main(String[] args) {
    //classloader会被用来验证是否可以加载传入的接口,
     SubjectInterface proxy = (SubjectInterface) Proxy.newProxyInstance(SubjectInterface.class.getClassLoader()
     , new Class[]{SubjectInterface.class}
     , new SubjectInvocationHandler(new RealSubject()));
     proxy.hi();
 }
复制代码

访问流程如下

代理模式与它在源码中的运用

生成代理proxy class阶段

代理对象会在内部缓存,如果没有缓存则会由 ProxyClassFactory 新生成。

首先会做接口校验,比如是否可以从提供的classLoader获取接口

Class<?> interfaceClass = null;
    try {
        interfaceClass = Class.forName(intf.getName(), false, loader);
    } catch (ClassNotFoundException e) {
    }
    if (interfaceClass != intf) {
        throw new IllegalArgumentException(
            intf + " is not visible from class loader");
    }
复制代码

验证完毕后,会读取接口的class文件使用的是 ProxyGenerator.generateProxyClass ,可以看到它会对整个的class文件的字节做读取

private byte[] generateClassFile() {
 ...
 dout.writeInt(0xCAFEBABE);
 ...
}
复制代码

最后调用native方法生成代理对象,并存入缓存

获取proxy的构造函数

构造函数指定的参数就是InvocationHandler

创建实例

调用构造函数,传入自定义的invocationHandler,自此生成了一个proxy实例,且实例本身会实现传入的接口,代码实例生成的对象如下所示

public final class $Proxy0 extends Proxy implements SubjectInterface {
    ...
       public final void hi() throws  {
        try {
           //这里的h即Proxy中的InvocationHandler,也就是用户自定义的InvocationHanlder
           //这个this对象代表的也就是 $Proxy0 本身
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    ...
}
复制代码

从JVM dump下动态代理的类

使用CGlib动态代理

引入CGlib之后,执行如下代码即可

public class Client {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(RealSubject.class);
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("cglib:"+method.getName());
                methodProxy.invokeSuper(o, objects);
                return o;
            }
        });
        RealSubject realSubject = (RealSubject)enhancer.create();
        realSubject.hi();
    }
}
复制代码

动态代理在spring aop中的运用

spring中默认使用 DefaultAopProxyFactory

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
			Class<?> targetClass = config.getTargetClass();
			if (targetClass == null) {
				throw new AopConfigException("TargetSource cannot determine target class: " +
						"Either an interface or a target is required for proxy creation.");
			}
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
			    //对于接口使用的是JDK
				return new JdkDynamicAopProxy(config);
			}
			//其余使用Cglib作为动态代理的实现
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			return new JdkDynamicAopProxy(config);
		}
	}
复制代码

JdkDynamicAopProxy

它实现了InvocationHandler和AopProxy。AopProxy主要是负责提供getProxy,实现为

@Override
	public Object getProxy() {
		return getProxy(ClassUtils.getDefaultClassLoader());
	}

	@Override
	public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isTraceEnabled()) {
			logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
		}
		Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
		findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
		return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
	}

复制代码

当有需要执行的方法的时候,则是执行invoke

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 ...
 	invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
	// Proceed to the joinpoint through the interceptor chain.
	retVal = invocation.proceed();
 ...
}

public Object proceed() throws Throwable {
    ...
    if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
    //这里会执行AfterReturningAdviceInterceptor、AspectJAfterThrowingAdvice等等方法
    	return dm.interceptor.invoke(this);
    }
    ...
}
复制代码

CglibAopProxy

它实现了AopProxy,核心的生成代理的方式如下

public Object getProxy(@Nullable ClassLoader classLoader) {
 ...
 Enhancer enhancer = createEnhancer();
 if (classLoader != null) {
		enhancer.setClassLoader(classLoader);
		if (classLoader instanceof SmartClassLoader &&
				((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
			enhancer.setUseCache(false);
		}
	}
	enhancer.setSuperclass(proxySuperClass);
	enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
	enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
	enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));

	Callback[] callbacks = getCallbacks(rootClass);
	Class<?>[] types = new Class<?>[callbacks.length];
	for (int x = 0; x < types.length; x++) {
		types[x] = callbacks[x].getClass();
	}
	// fixedInterceptorMap only populated at this point, after getCallbacks call above
	enhancer.setCallbackFilter(new ProxyCallbackFilter(
			this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
	enhancer.setCallbackTypes(types);

	// Generate the proxy class and create a proxy instance.
	return createProxyClassAndInstance(enhancer, callbacks);
 ...
}

private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
...
Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
...
Callback[] mainCallbacks = new Callback[] {
		aopInterceptor,  // for normal advice
		targetInterceptor,  // invoke target without considering advice, if optimized
		new SerializableNoOp(),  // no override for methods mapped to this
		targetDispatcher, this.advisedDispatcher,
		new EqualsInterceptor(this.advised),
		new HashCodeInterceptor(this.advised)
};
...
else {
	callbacks = mainCallbacks;
}
...
}
复制代码

以DynamicAdvisedInterceptor为例,它就实现了MethodInterceptor

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    ...
    //同样在此处去运行具体的Advice
    retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
    ...
}
复制代码

通常可以使用Proxy的情况

  1. 虚代理:根据需要创建开销很大的对象,只有用到时才创建
  2. 保护代理:控制对原始对象的访问
  3. 智能指引:在访问对象时附加了一些操作,比如对象没有引用时释放它
  4. 远程代理:为一个对象在不同的地址空间提供局部代表

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Redis设计与实现

Redis设计与实现

黄健宏 / 机械工业出版社 / 2014-6 / 79.00

【官方网站】 本书的官方网站 www.RedisBook.com 提供了书本试读、相关源码下载和勘误回报等服务,欢迎读者浏览和使用。 【编辑推荐】 系统而全面地描述了 Redis 内部运行机制 图示丰富,描述清晰,并给出大量参考信息,是NoSQL数据库开发人员案头必备 包括大部分Redis单机特征,以及所有多机特性 【读者评价】 这本书描述的知识点很丰富,......一起来看看 《Redis设计与实现》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具