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

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

内容简介:代理是指一个包装了真实访问对象的类,以便控制对真实类的访问访问流程如下此时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. 远程代理:为一个对象在不同的地址空间提供局部代表

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

查看所有标签

猜你喜欢:

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

JavaScript DOM编程艺术 (第2版)

JavaScript DOM编程艺术 (第2版)

[英] Jeremy Keith、[加] Jeffrey Sambells / 杨涛、王建桥、杨晓云 等 / 人民邮电出版社 / 2011-4 / 49.00元

JavaScript是Web开发中最重要的一门语言,它强大而优美。无论是桌面开发,还是移动应用。JavaScript都是必须掌握的技术。W3C的DOM标准是开发Web应用的基石。已经得到所有现代浏览器的支持,这使得跨平台Web开发成了一件轻松惬意的事。 本书是超级畅销书的升级版,由倡导Web标准的领军人物执笔,揭示了前端开发的真谛,是学习JavaScript和DOM开发的必读之作。 本......一起来看看 《JavaScript DOM编程艺术 (第2版)》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

URL 编码/解码
URL 编码/解码

URL 编码/解码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具