从静态代理到动态代理
代理模式
代理模式是非常常见的设计模式,在功能增强方面使用的特别明显,例如数据库连接池会使用代理连接代理真实的物理连接,以达到close只是归还到池中而不是真实关闭的效果。
模式介绍
代理模式可以说是特别容易上手的一个模式,因为现实生活中就有很多的代理,理解起来相对是比较容易的。
- 代理类实现了和实现类一样的接口
- 代理类依赖实现类
- 调用其实使用的是代理对象
模式优点和缺点
优点
当对现有功能增强的时候不需要修改已经实现的部分,只需要写代理类即可,满足了开闭原则。
缺点
当接口发生变化的时候,不但实现类要改动,代理类也要跟着改动。例如新增和修改了接口,代理类作为接口的实现类,也需要实现这些。
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是必须的了。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
用java做网站,java连接数据库并查询输出到页面
java web的后缀名是jsp,所以咱们要有一个jsp的开发环境,我这用的是jspStudy 自行百度。这软件是一个集成开发环境,安装启动后即可使用,集成了tomcat和mysql数据库首先我们先新建一个首页文件 index.jsp [java] view plain copy <%@pagelanguage="java"contentType="text/html;charset=utf-8"pageEncoding="UTF-8"%> <%@pageimport="java.sql.Connection"%> <%@pageimport="java.sql.DriverManager"%> <%@pageimport="java.sql.SQLException"%> <%@pageimport="java.sql.Statement"%> <%@pageimport="java.sql.PreparedStatement"%> <%@pageimport="java.sql.ResultSet"%&g...
- 下一篇
Java学习(13)--包/修饰符
一、包 概述:其实就是文件夹,不允许包名重复,一般是域名反写 作用:对类进行分类管理 操作:增删改查 分类:1.按模块 2.按功能 二、修饰符 (1)分类: 权限修饰符: private(私有的;只能在内部访问), 默认(default;包访问权限) , protected(受保护的;子类访问权限),public(公共的;所有的都可以访问) 状态修饰符: static,final 抽象修饰符: abstract 权限修饰符 (2)常见的类及其组成的修饰 类:默认(default) ,public,final,abstract 常用的: public 成员变量:private, 默认(default) ,protected,public,static,final 常用的: private 构造方法:private, 默认(default) ,protected,public 常用的: public 成员方法:private, 默认(default) ,protected,public,static,final,abstract 常用的: public (3)另外比较常见的: publ...
相关文章
文章评论
共有0条评论来说两句吧...