代理模式简介
一、概念
为某个对象提供一个代理,以控制对这个对象的访问。 代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象替代。代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理。
代理可以分为静态代理和动态代理
二、静态代理
由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
代理接口:
1 public interface BookStore { 2 3 public void buybook(int i); 4 5 public void querybook(); 6 7 }
委托类:
1 public class BookStoreImpl implements BookStore { 2 3 public static int count = 0; 4 5 public static int getCount() { 6 return count; 7 } 8 9 public static void setCount(int count) { 10 BookStoreImpl.count = count; 11 } 12 13 @Override 14 public void buybook(int i) { 15 count += i; 16 System.out.println("购买了" + i + "本书"); 17 18 } 19 20 @Override 21 public void querybook() { 22 System.out.println("手上有" + count + "本书"); 23 } 24 25 }
静态代理:
1 public class StaticProxyBookStore implements BookStore { 2 BookStore bs = new BookStoreImpl(); 3 4 @Override 5 public void buybook(int i) { 6 System.out.println("开始"); 7 bs.buybook(i); 8 System.out.println("结束"); 9 } 10 11 @Override 12 public void querybook() { 13 bs.querybook(); 14 } 15 16 }
测试类:
1 package ty.practice.dynmicTest; 2 3 public class ProxyTest2 { 4 public static void main(String[] args) { 5 StaticProxyBookStore proxy = new StaticProxyBookStore(); 6 proxy.buybook(10); 7 System.out.println("........."); 8 proxy.querybook(); 9 } 10 }
结果:
开始
购买了10本书
结束
.........
手上有10本书
优点:业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共有优点。
缺点:
1)代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
2)如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
三、JDK动态代理
动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定。
实现步骤:
a. 实现InvocationHandler接口创建自己的调用处理器
b. 给Proxy类提供ClassLoader和代理接口类型数组创建动态代理类
c. 以调用处理器类型为参数,利用反射机制得到动态代理类的构造函数
d. 以调用处理器对象为参数,利用动态代理类的构造函数创建动态代理类对象
代理类:
1 public class DynamicProxyBookStore implements InvocationHandler { 2 3 Object target; 4 5 public DynamicProxyBookStore(Object bs) { 6 this.target = bs; 7 } 8 9 @Override 10 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 11 System.out.println("开始"); 12 method.invoke(target, args); 13 System.out.println("结束"); 14 return null; 15 } 16 17 }
测试类:
1 public class ProxyTest { 2 public static void main(String[] args) { 3 BookStore bs = (BookStore)Proxy.newProxyInstance(BookStore.class.getClassLoader(), BookStoreImpl.class.getInterfaces(), new DynamicProxyBookStore(new BookStoreImpl())); 4 bs.buybook(10); 5 6 bs.querybook(); 7 } 8 }
结果:
开始
购买了10本书
结束
..........
开始
手上有10本书
结束
优点:
动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。在本示例中看不出来,因为invoke方法体内嵌入了具体的外围业务(记录任务处理前后时间并计算时间差),实际中可以类似Spring AOP那样配置外围业务。
缺点:
诚然,Proxy 已经设计得非常优美,但是还是有一点点小小的遗憾之处,那就是它始终无法摆脱仅支持 interface 代理的桎梏,因为它的设计注定了这个遗憾。
四、CGLIB动态代理
委托类:
1 public class BookStore { 2 public int count = 0; 3 4 public void buyBook(int i) { 5 System.out.println("购买了" + i + "本书"); 6 count += i; 7 } 8 9 public void query() { 10 System.out.println(count); 11 } 12 }
代理类:
1 public class CglibProxy implements MethodInterceptor { 2 private Enhancer enhancer = new Enhancer(); 3 4 public Object getProxy(Class clazz) { 5 // 设置需要创建子类的类 6 enhancer.setSuperclass(clazz); 7 enhancer.setCallback(this); 8 // 通过字节码技术动态创建子类实例 9 return enhancer.create(); 10 } 11 12 public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable { 13 System.out.println("前置代理"); 14 Object result = arg3.invokeSuper(arg0, arg2); 15 System.out.println("后置代理"); 16 return null; 17 } 18 19 }
测试类:
public class CglibTest extends TestCase { public void testCglib() { CglibProxy proxy = new CglibProxy(); // 通过生成子类的方式创建代理类 BookStore Bookproxy = (BookStore) proxy.getProxy(BookStore.class); Bookproxy.buyBook(10); Bookproxy.buyBook(20); Bookproxy.query(); } }
总结:CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。
参考:
http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/