从静态代理到动态代理


声明:本文转载自https://my.oschina.net/xpbob/blog/1633167,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

代理模式

代理模式是非常常见的设计模式,在功能增强方面使用的特别明显,例如数据库连接池会使用代理连接代理真实的物理连接,以达到close只是归还到池中而不是真实关闭的效果。

模式介绍

代理模式可以说是特别容易上手的一个模式,因为现实生活中就有很多的代理,理解起来相对是比较容易的。 输入图片说明

  1. 代理类实现了和实现类一样的接口
  2. 代理类依赖实现类
  3. 调用其实使用的是代理对象

模式优点和缺点

优点

当对现有功能增强的时候不需要修改已经实现的部分,只需要写代理类即可,满足了开闭原则。

缺点

当接口发生变化的时候,不但实现类要改动,代理类也要跟着改动。例如新增和修改了接口,代理类作为接口的实现类,也需要实现这些。

#java的动态代理 动态代理的出现解决了模式的缺点

动态代理的简易展示

class TestInvocationHanlder implements InvocationHandler{ 	ITest target; 	public TestInvocationHanlder(ITest target) { 		super(); 		this.target = target; 	} 	@Override 	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 		System.out.println("before"); 		method.invoke(target, args); 		System.out.println("after"); 		return null; 	} 	 
	public static void main(String[] args) { 		 		TestInvocationHanlder hanlder =new TestInvocationHanlder(new TestImpl()); 		ITest newProxyInstance = (ITest) Proxy.newProxyInstance(Main.class.getClassLoader(),new Class[]{ITest.class} , hanlder); 		newProxyInstance.hello(); 	} 

上面代码锁展示的部分就是一个动态代理的操作过程。
主要就是通过newProxyInstance产生代理对象。

InvocationHandler的作用

看了代码的人一个最大的疑问就是为什么调用的代码最后会走入到InvocationHandler的实现中去。 我们先来看看newProxyInstance是做了什么操作

    public static Object newProxyInstance(ClassLoader loader,                                           Class<?>[] interfaces,                                           InvocationHandler h)         throws IllegalArgumentException     {         final Class<?>[] intfs = interfaces.clone();         Class<?> cl = getProxyClass0(loader, intfs);             final Constructor<?> cons = cl.getConstructor(constructorParams);             final InvocationHandler ih = h;             return cons.newInstance(new Object[]{h});      } 

以上代码是去除了检验操作的部分,我们可以清晰的看到,这里是构造了一个cl的对象,并且把InvocationHandler 作为构造的参数传入的。

getProxyClass0是一个创造字节码并且类加载的过程。 我们可以通过ProxyGenerator看到动态字节码生成后的结果。

		byte[] proxyClassFile = ProxyGenerator.generateProxyClass("Proxy", new Class[] { ITest.class }); 		Files.copy(new ByteArrayInputStream(proxyClassFile), Paths.get("F:\\code\\Proxy.class")); 

使用如上代码,就可以生成一个名叫proxy的字节码文件

public final class Proxy extends java.lang.reflect.Proxy implements ITest {   private static Method m1;   private static Method m3;   private static Method m2;   private static Method m0;      public Proxy(InvocationHandler paramInvocationHandler)   {     super(paramInvocationHandler);   }      public final boolean equals(Object paramObject)   {       return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();   }      public final void hello()   {       this.h.invoke(this, m3, null);       return;   }      public final String toString()   {       return (String)this.h.invoke(this, m2, null);   }      public final int hashCode()   {       return ((Integer)this.h.invoke(this, m0, null)).intValue();   }      static   {       m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });       m3 = Class.forName("proxy.ITest").getMethod("hello", new Class[0]);       m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);       m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);    } } 

以上也是省去了部分代码的结果,我们看到proyx是一个继承了reflect并且实现了我们的接口的类,他的构造参数就是InvocationHandler ,我们可以看到里面的方法,都是直接用InvocationHandler 的invoke来调用的,具体的方法,上面的声明的method就是具体方法调用时,就是调用invoke传入的参数。

动态代理流程

java直接生成了一个实现接口的字节码,对应我们的代理类,然后具体的流程必须我们编写invocationhandler来调用,代理类就是调用invocationhandler,以此达到动态的效果。 输入图片说明

真实运行起来的效果就是上面的类图,只不过这个proxy是动态生成代理类的过程,由于代理类是根据接口动态生成的,所以在接口变化的时候,只要是逻辑没变的情况下,动态代理是不需要修改代码的。

总结

动态代理的本质就是动态生成代理类,他解决了接口变化带来的代码变化,例如接口新增了方法a,那么不用修改代码,代理就可以做到对a的增强。但是他无法解决对部分方法做增强的问题,例如原来的方法a,b,c,是对a是用a逻辑增强,对b有b逻辑增强。这块逻辑都要写在invocationhanlder的invok方法里,当遇到这种情况,修改invocationhandler是必须的了。

本文发表于2018年03月12日 22:38
(c)注:本文转载自https://my.oschina.net/xpbob/blog/1633167,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 1729 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1