JDK动态代理原理
2016-12-05 15:15
337 查看
静态代理
生活中代理的例子不胜枚举:张三由于工作很忙,迟迟没有结婚,家里人很着急。他爸爸拿着他的照片帮他张罗着相亲,他爸爸就是他的一个代理。
- 定义接口
public interface People { /** * 找对象 */ public void Zhaoduixiang(); }
张三
public class ZhangSan implements People{ public void Zhaoduixiang() { System.out.println(this.getClass().getName()+":要求不多,人美胸大活好!"); } }
张三他爸
public class ZhangSanFather implements People{ /** * 被代理对象的引用,相当于他爸拿着张三的照片 */ private ZhangSan zhangSan; public ZhangSanFather(){ } public ZhangSanFather(ZhangSan zhangSan){ this.zhangSan = zhangSan; } /** * 代理 */ public void Zhaoduixiang() { before(); this.zhangSan.Zhaoduixiang(); after(); } /** * 想跟我儿子好,我先要审核下 */ private void before(){ System.out.println(this.getClass().getName()+":要求人品好,家境好!"); } /** * 评价这个菇凉 */ private void after(){ System.out.println(this.getClass().getName()+":这个菇凉阔以!"); } }
测试
@Test public void testStaticProxy(){ People zhangSanFather = new ZhangSanFather(new ZhangSan()); zhangSanFather.Zhaoduixiang(); }
proxy.staticProxy.ZhangSanFather:要求人品好,家境好! proxy.staticProxy.ZhangSan:要求不多,人美胸大活好! proxy.staticProxy.ZhangSanFather:这个菇凉阔以!
重要特点:
- 代理类持有被代理类的引用
动态代理
张三没结婚,请个保姆做饭,洗碗,张三自己只用张嘴吃饭就行。保姆就是张三的代理。- 接口
public interface People { /** * 吃东西 */ void eat(String food) throws Throwable; }
张三
public class ZhangSan implements People{ /** * 吃 * @param food 食物 */ public void eat(String food) { System.out.println(this.getClass().getName()+":吃"+food+"!"); } }
代理
public class ProxyHandler implements InvocationHandler{ /** * 被代理对象的引用 */ private People people; public ProxyHandler(People people){ this.people = people; } /** * 代理方法 * @param proxy * @param method * @param args * @return * @throws Throwable */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(); method.invoke(people,args); after(); return null; } /** * 执行在代理方法之前 */ private void before(){ System.out.println(this.getClass().getName()+":做饭!"); } /** * 执行在代理方法之后 */ private void after(){ System.out.println(this.getClass().getName()+":洗碗!"); } }
测试
@Test public void testJDKDynamicProxy() throws Throwable { //创建代理 People people = (People) Proxy.newProxyInstance( People.class.getClassLoader(), new Class[]{People.class}, new ProxyHandler(new ZhangSan())); System.out.println(people.getClass().getName()); //执行操作 people.eat("炸鸡,喝啤酒 4000 !"); }
com.sun.proxy.$Proxy0 proxy.dynamicProxy.ProxyHandler:做饭! proxy.dynamicProxy.ZhangSan:吃炸鸡,喝啤酒!! proxy.dynamicProxy.ProxyHandler:洗碗!
到这里,效果虽然看到了,但是为什么要使用动态代理呢?
保姆给张三做饭、洗碗,大多时候不会像张三他爸那样长期固定,可能会因各种原因辞职,而重新帮另外的人做饭、洗碗。她代理的具体事情没有变化,变化的只是被代理的对象,如果使用静态代理,那么每次都要重新定义代理(不符合开闭原则),或者新增代理(代码冗余,不宜维护)。那么jdk动态代理又是如何工作的呢?
前面代码中System.out.println(people.getClass().getName());
打印的结果是
com.sun.proxy.$Proxy0
这就是jdk动态生成的,处于内存中的代理。还是一头雾水,必须要看看他具体的内容是什么
/** * 生成代理的Class文件 */ public static void createProxyClassFile(){ //生成代理类的字节流 byte[] $Proxy0s = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{People.class}); //生成文件 try { FileOutputStream fout = new FileOutputStream("src/main/java/proxy/dynamicProxy/$Proxy0.class"); fout.write($Proxy0s); fout.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
重新运行程序,生成代理文件
@Test public void testJDKDynamicProxy() throws Throwable { //创建代理 People people = (People) Proxy.newProxyInstance( People.class.getClassLoader(), new Class[]{People.class}, new ProxyHandler(new ZhangSan())); System.out.println(people.getClass().getName()); //执行操作 people.eat("炸鸡,喝啤酒!"); createProxyClassFile(); }
如上图,这个$Proxy0.class就是jdk动态生成的,反编译打开该文件,真相就在里面!
public final class $Proxy0 extends Proxy implements People { private static Method m1; private static Method m0; private static Method m3; private static Method m2; public $Proxy0(InvocationHandler var1) throws { super(var1); } public final boolean equals(Object var1) throws { ... } public final int hashCode() throws { ... } public final void eat(String var1) throws Throwable { super.h.invoke(this, m3, new Object[]{var1}); } public final String toString() throws { ... } static { try { ... m3 = Class.forName("proxy.dynamicProxy.People").getMethod("eat", new Class[]{Class.forName("java.lang.String")}); ... } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
从源码可以看出$Proxy0的真实身份:
1、它实际上实现了People接口,所以可以具体实现eat(String var1)方法。
2、而且它继承了Proxy类,在jdk源码中可以看到它有一个成员变量InvocationHandler,是不是很眼熟,这就是我们自定义代码ProxyHandler实现的接口。
public class Proxy implements java.io.Serializable{ ... protected InvocationHandler h; ... }
3、这里逻辑就很清晰了,eat(String var1)的具体实现就是调用InvocationHandler接口的invoke方法,并把真实的eat方法反射生成通过参数传入invoke方法,所以我们才能在ProxyHandler中调用method.invoke(people,args),从而实现动态代理。
动态代理完美的诠释的开闭原则,即对扩展开放,对修改关闭。非常适用于程序扩展,非常非常重要,在各种框架中广泛应用,如大名鼎鼎的SpirngAOP。
相关文章推荐
- JDK动态代理实现原理
- JDK动态代理干了什么事情?为什么我的事务会不起效?spring的事务到底应该怎么使用?原理是什么?
- JDK动态代理实现原理解析
- JDK动态代理实现原理
- JDK动态代理实现原理--转载
- JDK动态代理实现原理
- JDK动态代理实现原理
- JDK动态代理实现原理
- JDK动态代理实现原理
- Java之美[从菜鸟到高手演练]之JDK动态代理的实现及原理
- jdk动态代理实现原理
- 从原理出发理解JDK动态代理
- JDK动态代理实现原理
- JDK动态代理实现原理
- JDK的动态代理原理
- jdk动态代理实现原理
- JDK动态代理实现原理
- Java Aop原理--利用JDK动态代理
- JDK动态代理实现原理
- JDK动态代理实现原理