ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

代理模式

2020-09-17 13:00:38  阅读:160  来源: 互联网

标签:target 对象 Object 代理 模式 cglib public


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

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

 

标签:target,对象,Object,代理,模式,cglib,public
来源: https://www.cnblogs.com/whyha/p/13675943.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有