代理模式
代理模式的关键点在于代理类与被代理类在客户端看来是“相同”的,不管这种“相同”是通过继承还是接口来定义
代理模式的UML
代理模式的实现
静态代理: 静态代理指的是在程序运行前,代理类与委托类之间的代理关系就已经确定了,它们的字节码在运行就已经存在
动态代理:动态代理是指在程序运行的过程中,动态地生成指定类的代理类,并增加一些额外的实现逻辑, 根据实现方式,动态代理又可以进一步分为 继承方式实现(cglib) 与 接口方式实现(JAVA原生支持)
动态代理的实现
1. JAVA原生方式
JAVA原生方式实现动态代理主要涉及到两个类, Proxy和InvocationHandler, 每一个proxy实例都有一个关联的invocationHandler,所有对proxy实例的调用最终都会委托到相关联的invocationHandler的invoke方法中调用, 具体的调用时序图如下:
JAVA原生方式实现的动态代理示例代码如下:
从上面的示例代码中可以看出,原生方式实现的动态代理必须有一个前提: 委托类必须实现接口,否则无法通过这种方式进行代理.
2. CgLib的方式实现动态代理
cglib使用asm动态生成字节码,因此在使用cglib时需要增加asm的依赖. 另外,cglib使用了继承的方式来实现动态代理,因此 委托类不能声明成final,否则无法通过cglib进行代理
cglib实现动态代理主要涉及到两个类,Enhancer和MethodInterceptor.
Enhancer可以同时支持继承超类和实现接口两种方式来生成动态代理类, 通过设置超类和回调接口(Callback)可以实现与Proxy与InvocationHandler一样的效果.
MethodInterceptor是最常用的回调接口, 它可以认为是一种AroundAdvice, 也是拦截器的一种实现, 它只有一个方法intercept, 可以在这个方法中定义相应的代理实现逻辑.
除此之外,cglib还提供了CallbackFilter接口,它是用来匹配方法与callback接口的接口,可以为不同的方法定义不同的回调实现.
cglib实现的动态代理示例代码如下:
总结
JAVA原生的动态代理实现是基于接口的方式,因此被代理的类必须实现接口,否则无法通过这种方式实现动态代理
CgLib是通过继承方式实现动态代理,因此被代理的类不能是final. 另外,由于它是在字节码层面进行动态代理,因此依赖于asm库
不管是JAVA原生方式还是cglib方式实现动态代理,可以看出实现的思路均是通过回调或者拦截的方式修改被代理方法的实现逻辑来完成的(implementation and delegation)
动态代理模式也是实现框架最重要的一种模式,它也是spring框架中aop, 事务控制等特性的基础
代理模式与装饰器模式的区别在于,代理模式关注于对象的访问,而装饰器模式更关注动态地增加功能;另外,代理模式中代理类与被代理类的代理关系一般在编译时就已经确定,而装饰器模式的目标对象可以在运行时指定.