代理模式

为其他对象提供一种代理以控制对这个对象的访问。

代理模式分为:静态代理、动态代理(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类。

 

上一篇:23种设计模式总结


下一篇:Java 动态代理