不学无数——Java动态代理
动态代理
1. 什么是动态代理
在上一章节中,我们讲的是代理其实都是静态代理,动态代理是在运行阶段动态的创建代理并且动态的处理对所代理方法的调用。在动态代理上所做的所有调用都会被重定向到单一的调用处理器中。在现在很流行的Spring中有一个AOP(面向切面)的其中核心实现技术就是动态代理的技术。
2. 为什么要用动态代理
动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。例如我们想计算出每一个方法的执行时间,如果使用静态代理的话,那么就需要在每一个代理类中进行更改,但是如果使用了动态代理可以对类的所有方法进行统一的管理。一处添加,所有方法适用。
3. 动态代理的简单实现
3.1 静态代理的实现
我们先看一下静态代理的是如何实现的,关于静态代理详细的解释可以看不学无数——Java代理模式,这里只贴出关于静态代理的一些代码。
Homeowner
接口如下:
interface Homeowner{ public void LeaseHouse(Home home); }
RealHomeowner
类如下
class RealHomeowner implements Homeowner{ @Override public void LeaseHouse(Home home) { System.out.println("房价是: "+ home.getPrice()); System.out.println("房子颜色是: "+ home.getColor()); System.out.println("房子出租成功"); } }
代理类HomeProxy
的实现
class HomeProxy implements Homeowner{ private Homeowner homeowner; public HomeProxy(Homeowner homeowner){ this.homeowner = homeowner; } @Override public void LeaseHouse(Home home) { System.out.println("中介干预"); homeowner.LeaseHouse(home); System.out.println("中介干预完成"); } }
在main方法中使用
public static void main(String[] args) { Home home = new Home("red",1000); RealHomeowner realHomeowner = new RealHomeowner(); Homeowner homeowner = new HomeProxy(realHomeowner); homeowner.LeaseHouse(home); }
打印的信息如下:
中介干预 房价是: 1000 房子颜色是: red 房子出租成功 中介干预完成
3.2 动态代理的实现
在动态代理中是不需要代理类的,就是不需要上面静态代理中的HomeProxy
类,通过实现了InvocationHandler
类,所有方法都由该'Handler'来处理了,意思就是所有被代理的方法都由InvocationHandler
接管实际的处理任务。那么看实际的例子
DynamicPro
类
class DynamicPro implements InvocationHandler{ //真实被代理的实例对象 private Object object; public DynamicPro(Object object){ this.object = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("中介干预"); Object result = method.invoke(object,args); System.out.println("中介干预完成"); return result; } }
在主方法如下的调用
public static void main(String[] args) { Home home = new Home("red",1000); //创建一个被代理的实例对象 RealHomeowner realHomeowner = new RealHomeowner(); //创建一个与被代理对象相关的InvocationHandler DynamicPro dynamicPro = new DynamicPro(realHomeowner); //创建一个类加载器 ClassLoader classLoader = realHomeowner.getClass().getClassLoader(); //被代理类的接口数组,里面的每一个方法都会执行InvocationHandler中的invoke方法 Class<?>[] proxInterface = realHomeowner.getClass().getInterfaces(); Homeowner homeowner = (Homeowner) Proxy.newProxyInstance(classLoader,proxInterface,dynamicPro); homeowner.LeaseHouse(home); }
打印如下
中介干预 房价是: 1000 房子颜色是: red 房子出租成功 中介干预完成
4. 动态代理和静态代理的区别
上面是关于动态代理的类图,我们可以和静态代理的类图进行对比一下
可以看到在动态代理中不需要了实际的代理角色类,因为实际的代理角色在动态代理中时动态生成的。在动态代理中增加了InvocationHandler
接口类,这个接口中只有一个方法,就是invoke()
方法。我们可以实现InvocationHandler
类然后在invoke()
方法中对调用实际方法时的前置或者后置处理。
5. 动态代理原理简单分析
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); final Class<?>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * Look up or generate the designated proxy class. */ Class<?> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); } return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } }
通过上面的代码的Debug发现最后是通过下面代码返回一个对象的
//返回构造器生成的实例对象 return cons.newInstance(new Object[]{h});
然后发现cons是从下面的代码获得的
//获得此代理类的构造器 final Constructor<?> cons = cl.getConstructor(constructorParams);
cl是从下面的代码中获得的
//查找或生成指定的代理类 Class<?> cl = getProxyClass0(loader, intfs);
而intfs是从下面的代码获得
//克隆一个接口类 final Class<?>[] intfs = interfaces.clone();
随后想进去看getProxyClass0
生成的代理类是什么,但是发现进不去。后来查资料知道它由于是动态生成的,类是缓存在java虚拟机中的,可以通过下面的方法将类打印出来。
动态代理类的字节码在程序运行时由Java反射机制动态生成
public static void main(String[] args) { byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", Homeowner.class.getInterfaces()); String path = "/Users/hupengfei/git/Test/src/main/java/Practice/Day06/Homeowner.class"; try(FileOutputStream fos = new FileOutputStream(path)) { fos.write(classFile); fos.flush(); System.out.println("代理类class文件写入成功"); } catch (Exception e) { System.out.println("写文件错误"); } }
对生成的class文件进行反编译,在Idea中能直接查看
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // import Practice.Day06.Home; import Practice.Day06.Homeowner; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class $Proxy0 extends Proxy implements Homeowner { private static Method m1; private static Method m4; private static Method m2; private static Method m3; private static Method m0; public $Proxy0(InvocationHandler var1) throws { super(var1); } public final boolean equals(Object var1) throws { try { return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final void LeaseHouse(Home var1) throws { try { super.h.invoke(this, m4, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final Homeowner getProxy() throws { try { return (Homeowner)super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final int hashCode() throws { try { return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m4 = Class.forName("Practice.Day06.Homeowner").getMethod("LeaseHouse", Class.forName("Practice.Day06.Home")); m2 = Class.forName("java.lang.Object").getMethod("toString"); m3 = Class.forName("Practice.Day06.Homeowner").getMethod("getProxy"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
首先我们可以先看生成的此类的构造函数
public $Proxy0(InvocationHandler var1) throws { super(var1); } -----调用了父类的构造函数,而它的父类是Proxy类,父类的构造函数如下 protected Proxy(InvocationHandler h) { Objects.requireNonNull(h); this.h = h; } --在父类中的h的定义如下 protected InvocationHandler h;
此时我们就知道为什么我们的动态代理都会执行传入的InvocationHandler
中的invoke()
方法了
在下面的静态代码块中我们发现了LeaseHouse()
方法
m4 = Class.forName("Practice.Day06.Homeowner").getMethod("LeaseHouse", Class.forName("Practice.Day06.Home"));
然后在上面会发现有我们的方法
public final void LeaseHouse(Home var1) throws { try { super.h.invoke(this, m4, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } }
此时我们再回想一下在InvocationHandler
类中的invoke()
方法中传入的参数有Method
方法了,这样就可以将外部对于被代理对象的调用都转化为调用invoke()
方法,再由invoke()
方法中调用被代理对象的方法。
6. 参考文章
- https://www.cnblogs.com/gonjan-blog/p/6685611.html
- 设计模式之禅
- Java编程思想

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Andrew Ng经典机器学习课程的Python实现(第1部分)
几个月前,我在Coursera(免费大型公开在线课程项目)上完成Andrew Ng机器学习的MOOC教学。对于任何一个想进入人工智能和机器学习世界的人来说,这都是一个很好的入门课程,但其中的项目是用Octave语言编写的。我一直想知道这门课如果用Python的话该有多么神奇,最终我决定重做一遍,这次用Python来完成。 在这一系列的博文中,我打算用Python编写程序。这么做有以下几个原因: 1、这会帮助那些想要Python版本课程的人; 2.、对于有些R语言爱好者来说,他们也愿意学习熟悉的那些算法的Python实现,那会受益匪浅; 基础知识 强烈建议你先看第1周的视频讲座,之后就应该对Python的体系结构有基本的了解。 在这一节中,我们将研究最简单的机器学习算法。 仅有一个变量的线性回归 首先是关于场景的描述。在这里,我们将仅用一个变量来执
- 下一篇
十年后的程序员是否还是一个高薪职业?
10年前我刚硕士毕业,进一个小公司写C++,毕业生起薪就已经超过了澳洲平均年薪。 10年后的今年我拿多少钱就不说了。可以透露的行情是,在澳洲市场上,资深程序员(senior developer)一个人的年收入顶其他行业一个家庭的年收入稀松平常。那些光鲜行业从业人员比如房产中介,辛辛苦苦赚底薪+加成,还不如程序员躺拿钱赚得多。 并且在一定的级别上还有职位和薪水倒挂的现象。技术首席(tech lead)不一定比资深程序员(senior)拿得高;资深程序员很有可能比项目经理拿得高,虽然后者行政级别比前者高;而同个公司里资深程序员一定比分析师(business analyst)拿得高,虽然这两者基本是同级的。谁能干活能战斗,市场还是看的很清楚的。 而IT行业在这十年中的爆点是大规模增长的。10年前没有移动计算(iPhone App Store是2008年才出现的),没有平板电脑,没有云计算,没有VR,没有自动驾驶汽车,没有共享经济。现在呢?10年后呢?要用发展的眼光看问题。 狂喷IT业的无非也就是那么几点,什么写程序都是1个月里培训班出来骗钱的,互联网都是泡沫,等等。。。...
相关文章
文章评论
共有0条评论来说两句吧...