最近一段时间在看spring的源码,发现里面大量的使用的代理。代理:就是用代理类实现委托类的一些功能和附加的预处理功能(包括消息过滤,日志等)。代理(proxy)模式:指目标对象给定代理对象,并由代理对象代替真实对象控制客户端对真 实对象的访问, java的代理分为静态代理和动态代理,java 对动态代理有很好的支持,提供了 InvocationHandler接口和 Proxy 类。
1.动态代理和静态代理的区别
静态代理,代理类要实现被代理接口的所有的方法。
动态代理是利用java反射机制,代理类只需要实现InvocationHandler接口即可,无需实现接口所有方法,在扩展和代码优化上有明显优势。
2.静态代理
我们知道在java中要实现一个类中得功能,有继承父类和实现接口两种方式,也就是根据这一点静态代理可以分为继承代理和接口代理。
2.1继承代理
由于代理的功能一般最好没有实现,否则代理就没有价值了,所以被代理类(委托类)最好是abstract,也就出现了继承代理。
继承代理实现代码
package com.meituan; /** * 抽象类 * Created by zsq on 15/5/18. */ public abstract class Dao { public abstract void add(); public abstract void delete(); } /** * mysqlao实现Dao * Created by zsq on 15/5/18. */ public class MysqlDao extends Dao{ @Override public void add() { System.out.println("insert into mysql datebase!"); } @Override public void delete() { System.out.println("delete from mysql datebase!"); } } /** * oracleDao实现Dao * Created by zsq on 15/5/18. */ public class OracleDao extends Dao { @Override public void add() { System.out.println("insert into oracle datebase!"); } @Override public void delete() { System.out.println("delete from oracle datebase!"); } } /** * Dao的代理类 * Created by zsq on 15/5/18. */ public class DaoProxy extends Dao { private Dao dao; public DaoProxy(Dao dao) { this.dao = dao; } @Override public void add() { dao.add(); } @Override public void delete() { dao.delete(); } } /** * 测试类 * Created by zsq on 15/5/18. */ public class DaoTest { public static void main(String[] args) { Dao mysqldao=new MysqlDao(); Dao oracledao=new OracleDao(); DaoProxy p=new DaoProxy(mysqldao); //DaoProxy p=new DaoProxy(oracledao); p.add(); } } 输出:insert into mysql datebase! |
2.2接口代理
实现方式和继承代理差别不大,只是将abstract换成interface,将extends换成implements
3.动态代理
InvocationHandler 是代理实例的调用处理程序 实现的接口
InvocationHandler
1 2 3 4 5 6 7 |
package java.lang.reflect; public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; } |
动态代理是利用java反射机制,代理类通过实现InvocationHandler接口来完成对委托类功能的实现,其实里面是jdk对java的部分代码进行了封装,在实现InvocationHandler接口的时候,需要实现invoke方法,而invoke方法里需要传进2个必要参数代理类和Method,其中method里的invoke方法就可以去实现委托类里所有的接口,因为有了这层封装所以代理类不需要在继承或实现被代理类也可以实现代理功能。
我记得之前看过一个例子,很好的讲述了代理:
公司项目部需要 CEO 签署一个文件,项目负责人会把文件交给 CEO 助理,助理会收文件,等到 CEO 回来后递 CEO,CEO 签署后交给助理,助理收好交给项目负 责人。这个过程中项目负责人其实不知道是否真的是 CEO 签署的文件,有可能是助理打印的 CEO 的签名到文件上。这样助理就是一个代理角色,代替 CEO 处理事务。
代码如下:
动态代理举例
Collapse source
package com.meituan.proxy; /** * 抽象接口 * Created by zsq on 15/5/18. */ public interface Leader { public void sign(); } /** * 实现类 * Created by zsq on 15/5/18. */ public class CEO implements Leader { @Override public void sign() { System.out.println("CEO 签文件"); } } /** * 动态代理实现类 * Created by zsq on 15/5/18. */ public class Assistant implements InvocationHandler { private Leader leader; public Assistant(Leader leader) { this.leader = leader; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("递给领导"); Object obj = method.invoke(leader, args); System.out.println("装入袋子,送出"); return obj; } } /** * 测试类 * Created by zsq on 15/5/18. */ public class DynamicTest { public static void main(String[] args) { Leader leader = new CEO(); Assistant proxy = new Assistant(leader); //创建动态代理实例 Leader leader1 = (Leader) Proxy.newProxyInstance(Leader.class.getClassLoader(), new Class[]{Leader.class}, proxy); leader1.sign(); } } 输出: 递给领导 CEO 签文件 装入袋子,送出 |
4.代理的作用
主要用来做方法的增强,让你可以在不修改源码的情况下,增强一些方法,在方法执行前后做任何你想做的事情(甚至根本不去执行这个方法),因为在InvocationHandler的invoke方法中,你可以直接获取正在调用方法对应的Method对象,具体应用的话,比如可以添加调用日志,做事务控制等。
还有一个有趣的作用是可以用作远程调用,比如现在有Java接口,这个接口的实现部署在其它服务器上,在编写客户端代码的时候,没办法直接调用接口方法,因为接口是不能直接生成对象的,这个时候就可以考虑代理模式(动态代理)了,通过Proxy.newProxyInstance代理一个该接口对应的InvocationHandler对象,然后在InvocationHandler的invoke方法内封装通讯细节就可以了。具体的应用,最经典的当然是Java标准库的RMI,比如各种webservice框架中的远程调用,大致都是这么实现的。
其实,我们在不知不觉中就会使用代理了,例如Spring中的AOP,Struts2中的拦截器等。
总结代理的作用:
- Proxy类的代码量被固定下来,不会因为业务的逐渐庞大而庞大;
- 动态代理可以实现AOP编程,实际上静态代理也可以实现,总的来说,AOP可以算作是代理模式的一个典型应用;
- 解耦,通过参数就可以判断真实类,不需要事先实例化,更加灵活多变。