一、静态代理:
假设原来有一个实现了指定接口/抽象类的子类:
class RealSubject implements Subject{ public void request(){ System.out.print("real request handling\n"); } }现在有两种情况会发生:
1新的代码需要调用Subject接口,但是需要给每个接口加入新代码(比如日志记录、权限控制等);
2旧的代码已经使用了Subject接口,需要不改动现有代码情况下加入新的代码(比如日志记录,权限控制);
对于这这两种情况,最直接的想法就是重新写一个代理类,同样实现Subject接口,针对RealSubject的所有接口进行封装,加入新的代码:
/************************************************ * 静态代理,封装(控制)request方法的访问 ************************************************/ class StaticProxySubject implements Subject{ private RealSubject realSubject; //组合被代理的对象 public void request(){ //do some thing else... if(realSubject == null){ realSubject = new RealSubject(); } realSubject.request(); //真正的request调用 //do some thing else... }
1面向Subject接口编程;
2创建Subject实例时采用工厂模式,通过读取配置文件决定反射哪个类的实例;
这样在创建一个新的静态代理时,才可以通过修改配置文件无缝集成到现有代码,修改行为而无需改动现有代码。
//通过静态代理来访问对象 Subject proxy = new StaticProxySubject(); proxy.request();
二、面向方面编程(AOP)
其实上面所说的第二种情况就是实现面向方面的编程的方式之一(将切面代码至于原始方法外面),在不改动现有代码的情况下,为所有业务横向插入公共逻辑(常见的如安全、事务控制、日志等),将交叉业务模块化。
三、动态代理
很明显静态代理有个大问题,就是一旦原始目标类接口过多,要为每个接口进行静态代理封装,那代价也很大。
因此Java中提出一个动态代理的概念,动态代理需要实现Java中的InvocationHandler接口:
/*********************************************************** * 动态代理. java.lang.reflect包中有自己的代理支持,利用这个包 * 你可以在运行时动态创建一个代理类,实现一个或者多个接口,并将 * 方法的调用转发到你所指定的类。 * 因为实际的代理类是在运行时创建的,所以我们称这个技术为动态代理。 * ***********************************************************/ class DynamicProxySubject implements InvocationHandler{ private Object realSubject; //组合被代理的对象 public DynamicProxySubject(Object realSubject){ this.realSubject = realSubject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //do some thing else... return method.invoke(realSubject, args); //调用对象realSubject的method方法 //do some thing else... } }
RealSubject real = new RealSubject(); Subject subject = (Subject) Proxy.newProxyInstance( real.getClass().getClassLoader(), //定义代理类的类加载器 real.getClass().getInterfaces(), //代理类要实现的接口列表 new DynamicProxySubject(real)); //指派方法调用的调用处理程序 subject.request();
(1)其中最后一个参数是动态代理类对象,组合了其真正要代理的类。
(2)倒数第二个参数需要传递要代理的真正的类的接口信息(Class信息),即Subject接口信息,这样返回的代理就是Subject类型的。
这样每次调用返回的Subject类型代理对象的接口时,都会转换为针对动态代理类中invoke方法的调用,在invoke方法中又调用真正代理的类的方法,我们可以将横切代码(日志、权限控制等)至于invoke方法中实现AOP。
四、动态代理原理
关键在于创建动态代理的newProxyInstance方法:
RealSubject real = new RealSubject(); Subject subject = (Subject) Proxy.newProxyInstance( real.getClass().getClassLoader(), //定义代理类的类加载器 real.getClass().getInterfaces(), //代理类要实现的接口列表 new DynamicProxySubject(real)); //指派方法调用的调用处理程序 subject.request();
倒数第二个参数是要代理的类的接口(Subject)的Class对象。第三个参数是动态代理类。
newProxyInstance方法返回一个自动生成类对象,该类实现了Subject接口,同时继承自Java Proxy类:
class Proxy{ InvocationHandler h=null; protected Proxy(InvocationHandler h) { this.h = h; } ... }
这个自动生成的类为什么能够实现Subject接口,答案是反射机制。
通过反射可以知道该接口所有方法,然后重写这些方法,简单地调用父类Proxy的动态代理属性的invoke方法即可。
publicfinal void request() { try { super.h.invoke(this,m3, null); return; } catch (Error e) { } catch (Throwable throwable) { throw newUndeclaredThrowableException(throwable); } }