为其他对象提供一种代理以控制对这个对象的访问。
代理模式分为:静态代理、动态代理(JDK代理、cglib代理)
解决问题:
在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
UML:
静态代理:
代理对象和被代理对象需要实现相同的接口或继承自相同的父类
1 // 抽象接口 2 interface IApp { 3 void open(); 4 } 5 6 public class AppImpl implements IApp { 7 8 @Override 9 public void open() { 10 System.out.println("打开APP"); 11 } 12 } 13 14 // 代理类 15 public class AppProxy implements IApp{ 16 17 private IApp target; 18 19 public AppProxy(IApp target) { 20 this.target = target; 21 } 22 23 @Override 24 public void open() { 25 System.out.println("查询APP路径"); 26 // 调用被代理对象的方法 27 target.open(); 28 // System.out.println("xxx"); 29 } 30 }View Code
Client:
1 public static void main(String[] args) { 2 // 被代理对象 3 AppImpl app = new AppImpl(); 4 //代理对象 5 AppProxy appProxy = new AppProxy(app); 6 7 appProxy.open(); 8 }View Code
运行结果:
缺点:需要创建多个代理类,增加阅读的复杂性
如果接口增加方法、代理对象和目标对象都需要修改
动态代理:
JDK代理:
代理对象不需要实现接口,但是目标对象需要实现接口
代理对象利用JDK的API动态生成,代理类:java.lang.reflect.Proxy
JDK代理的实现只需要使用以下方法:
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h):
loader:目标对象的类加载器
interfaces:代理类实现的接口列表
h:由代理实例的调用处理程序实现的接口,当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的 invoke 方法
接口及实现类:
1 interface IApp { 2 void open(); 3 } 4 5 public class AppImpl implements IApp { 6 @Override 7 public void open() { 8 System.out.println("打开APP"); 9 } 10 }View Code
代理类:
1 public class ProxyFactory { 2 3 private Object target; 4 5 public ProxyFactory(Object target) { 6 this.target = target; 7 } 8 9 public Object getProxyInstance() { 10 return Proxy.newProxyInstance(target.getClass().getClassLoader(), 11 target.getClass().getInterfaces(), 12 new InvocationHandler() { 13 @Override 14 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 15 System.out.println("JDK代理开始"); 16 Object res = method.invoke(target, args); 17 System.out.println("JDK代理结束"); 18 return res; 19 } 20 }); 21 } 22 }View Code
client:
1 public static void main(String[] args) { 2 // 被代理对象 3 IApp app = new AppImpl(); 4 //代理对象 5 IApp proxyInstance = (IApp) new ProxyFactory(app).getProxyInstance(); 6 System.out.println("proxyInstance = " + proxyInstance.getClass()); 7 8 proxyInstance.open(); 9 }View Code
缺点:JDK代理只能代理接口,不能对类进行代理。并且,如果实现类中新增了接口中没有的方法,这些方法是无法被代理的(因为无法被调用)。
cglib代理:
cglib是针对类来实现代理的,对代理的目标类生成一个子类,并覆盖其中方法实现增强,因为底层是基于创建被代理类的子类,所以它避免了JDK动态代理类的缺陷。但因为采用的是继承,所以不能代理final类。(final类不可被继承)
导入Maven依赖:
cglib 是基于asm 字节修改技术。导入 cglib 会间接导入 asm, ant, ant-launcher 三个jar 包
1 <dependency> 2 <groupId>cglib</groupId> 3 <artifactId>cglib</artifactId> 4 <version>3.2.5</version> 5 </dependency>View Code
实现类:
1 public class App { 2 public void open() { 3 System.out.println("打开APP"); 4 } 5 }View Code
代理类:
1 import java.lang.reflect.Method; 2 3 import net.sf.cglib.proxy.Enhancer; 4 import net.sf.cglib.proxy.MethodInterceptor; 5 import net.sf.cglib.proxy.MethodProxy; 6 7 public class ProxyFactory implements MethodInterceptor { 8 9 //维护一个目标对象 10 private Object target; 11 12 //构造器,传入一个被代理的对象 13 public ProxyFactory(Object target) { 14 this.target = target; 15 } 16 17 //返回一个代理对象: 是 target 对象的代理对象 18 public Object getProxyInstance() { 19 //1. 创建一个工具类 20 Enhancer enhancer = new Enhancer(); 21 //2. 设置父类 22 enhancer.setSuperclass(target.getClass()); 23 //3. 设置回调函数 24 enhancer.setCallback(this); 25 //4. 创建子类对象,即代理对象 26 return enhancer.create(); 27 } 28 29 //重写 intercept 方法,会调用目标对象的方法 30 @Override 31 public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable { 32 System.out.println("cglib代理开始"); 33 Object res = method.invoke(target, args); 34 System.out.println("cglib代理提交"); 35 return res; 36 } 37 }View Code
client:
1 public static void main(String[] args) { 2 // 被代理对象 3 App target = new App(); 4 //代理对象 5 App proxyInstance = (App) new ProxyFactory(target).getProxyInstance(); 6 7 proxyInstance.open(); 8 }View Code
由于是继承方式,static方法,private方法,final方法等不能被代理
cglib会默认代理Object中equals、toString、hashCode、clone等方法。比JDK代理多了clone
总结:
静态代理在编译时产生class字节码文件,可以直接使用,效率高。
JDK代理必须实现 InvocationHandler 接口,通过 invoke 调用被代理类接口方法是通过反射的方式,比较消耗系统性能,但可以减少代理类的数量,使用更灵活。
cglib代理无需实现接口,通过生成类字节码实现代理,比反射稍快,不存在性能问题,应用更加广泛。但cglib会继承目标对象,需要重写方法,所以目标对象不能为final类。