插件化开发笔记(一)代理模式

微信扫一扫,分享到朋友圈

插件化开发笔记(一)代理模式

前言

插件化开发所涉及到的技术点非常多,比如程序的启动流程、四大组件启动流程、ClassLoader原理、上下文Context、AMS原理、反射、代理等。本篇主要简单介绍代理模式(实际上只是一篇学习笔记),为后面介绍插件化实现做知识铺垫。

一、定义

定义:为其他对象提供一种代理,以控制对这个对象的访问,这种形式称为代理模式。(看起来挺抽象的,不好理解,能理解下面的解释就够了)

代理模式也叫委托模式,是结构性设计模式的一种。在现实生活中,我们用到类似代理模式的场景非常多,比如买房者找房屋中介买房、受害者委托律师打官司、老板安排助理采购等。这些场景有一个共同特点:真正想做某件事的人 ,因为行业壁垒等各种原因自己不容易完成,于是转而找其他专业人士来替自己完成这个意愿。以老板安排助理采购电脑设备为例:老板(即委托者)是真是需要购买电脑设备的人,但由于缺乏对电脑设备的了解,于是委派助理(即代理)来完成自己的这次采购意愿。

二、角色及结构

在代理模式中,包含了如下的角色:

Subject:抽象主题类,声明真实主题与代理的共同接口方法。也就是定义一个接口,定义老板和助理的这次行为:采购!

RealSubject:真实主题类,定义了代理所表示的集体对象,客户端通过代理类间接调用真实主题类的方法。即老板,真正需要采购的人。

Proxy:代理类,持有对真实主题类的引用,在其所实现的接口方法中调用真实主题类中相应的接口方法执行。即助理,以老板的名义去采购。

Client:客户端类。也就是一段逻辑代码,将上述角色组织起来,完成这次采购行为。

类结构图如下:

三、代码实现

从编码的角度来说,代理模式分为静态代理和动态代理,可以结合下面的例子来理解。(1)静态代理,在代码运行前代理类的class文件就已经存在了,结合下面的代码直观的理解就是,代理类Assistant类是写死的,在运行前这个编译类Assistant.class就存在了。(2)动态代理,则是在代码运行时才会通过反射来动态地生成代理类对象,并确定到底来代理谁。也就是说,我们在编码阶段并不会定义出这个代理类,而是在运行的时候动态生成。从下面的代码实现中,我们发现,动态代理实现时,并没有出现Assistant这个类。

下面我们来用代码实现静态代理和动态代理。

1、静态代理

1 //Subject:抽象主题类,定义了老板和助理的这次行为
2 public interface IShop {
3     void buy();
4 }
5
6 //RealSubject:真实主体类。实现了抽象主题接口
7 public class Boss implements IShop {
8     @Override
9     public void buy() {
10         System.out.println("I am boss,I buy buy buy");
11     }
12 }
13
14 //Proxy:代理类。以组合的方式持有了对真实主题类Boss的引用,也实现了抽象主题接口。在实现的buy方法中,调用了真实主题Boss的对应方法。
15 public class Assistant implements IShop {
16     private IShop mBoss;
17     Assistant(IShop shoper) {
18         mBoss = shoper;
19     }
20
21     @Override
22     public void buy() {
23         mBoss.buy();
24     }
25 }
26
27 //Client:客户端类
28 public class ProxyDemo {
29     public static void main(String[] args) {
30         IShop boss = new Boss();
31         IShop assitant = new Assistant(boss);
32         assitant.buy();
33     }
34 }

运行结果

I am boss,I buy buy buy

这里我们可以看到,Client类中调用的是代理Assistant的buy方法,而实际完成的是委托者Boss的buy方法,从而实现了这次代理行为。

2、动态代理

1 public interface IShop {
2     void buy();
3 }
4
5 public class Boss implements IShop {
6     @Override
7     public void buy() {
8         System.out.println("I am boss,I buy buy buy");
9     }
10 }
11
12 //Java提供了动态的代理接口InvocationHandler,实现该接口需要重写invoke方法。
13 import java.lang.reflect.InvocationHandler;
14 import java.lang.reflect.Method;
15
16 public class DynamicProxy implements InvocationHandler {
17     private Object mObject;
18
19     DynamicProxy(Object object) {
20         mObject = object;
21     }
22
23     @Override
24     public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable {
25         System.out.println("invoke methodName=" + method.getName());
26         method.invoke(mObject, objects);
27         return null;
28     }
29 }
30
31 //Client
32 import java.lang.reflect.InvocationHandler;
33 import java.lang.reflect.Proxy;
34
35 public class Client {
36     public static void main(String[] args) {
37         //创建boss类
38         IShop boss = new Boss();
39         //创建动态代理
40         InvocationHandler proxyHandler = new DynamicProxy(boss);
41         ClassLoader classLoader = boss.getClass().getClassLoader();
42         //动态创建代理类
43         IShop assitant = (IShop) Proxy.newProxyInstance(classLoader, new Class[]{IShop.class}, proxyHandler);
44         assitant.buy();
45     }
46 }

运行结果

invoke methodName=buy
I am boss,I buy buy buy

在动态代理类DynamicProxy中,声明了一个Object的引用,该应用指向被代理类对象(该实例中指向的就是Boss类对象),我们调用被代理类对象(Boss对象)的具体方法(该例子中指buy方法)会在invoke方法中执行。在Client类中的Proxy.newProxyInstance()来生成动态代理类对象assistant,而调用assistant.buy()方法时会调用DynamicProxy类中的invoke方法,这样就间接地调用了Boss类中的buy()方法,从而实现了动态代理。

四、静态代理和动态代理对比

说到使用动态代理的好处,可能得一大篇文章才说得清楚,在这里我只想提一点,就是动态代理下,直到运行时才会生成代理类,当代理场景比较多时,可以节约很多不必要的浪费。这里我们比较一下,有多个代理场景时,静态代理和动态代理的表现。

1、静态代理实现多个代理场景

1 public interface IShop {
2     void buy();
3 }
4
5 public class Boss implements IShop {
6     @Override
7     public void buy() {
8         System.out.println("I am boss,I buy buy buy");
9     }
10 }
11
12 public class Assistant implements IShop {
13     private IShop mBoss;
14     Assistant(IShop shoper) {
15         mBoss = shoper;
16     }
17
18     @Override
19     public void buy() {
20         mBoss.buy();
21     }
22 }
23
24 public interface IDrive {
25     void drive();
26 }
27
28 public class Leader implements IDrive {
29     @Override
30     public void drive() {
31         System.out.println("I am leader,I drive drive drive");
32     }
33 }
34
35 public class Driver implements IDrive {
36     private IDrive mLeader;
37
38     Driver(IDrive driver) {
39         mLeader = driver;
40     }
41
42     @Override
43     public void drive() {
44         mLeader.drive();
45     }
46 }
47
48 public class ProxyDemo {
49     public static void main(String[] args) {
50         IShop boss = new Boss();
51         IShop assitant = new Assistant(boss);
52         assitant.buy();
53
54         IDrive leader = new Leader();
55         IDrive driver = new Driver(leader);
56         driver.drive();
57     }
58 }

运行结果

I am boss, I buy buy buy
I am leader,I drive drive drive

如上代码实际上就是两个代理场景的叠加,增加一个场景,代码量增加一倍。

2、动态代理实现多个代理场景

1 public interface IShop {
2     void buy();
3 }
4
5 public class Boss implements IShop {
6     @Override
7     public void buy() {
8         System.out.println("I am boss,I buy buy buy");
9     }
10 }
11
12 public interface IDrive {
13     void drive();
14 }
15
16 public class Leader implements IDrive {
17     @Override
18     public void drive() {
19         System.out.println("I am leader,I drive drive drive");
20     }
21 }
22
23 //动态代理类
24 import java.lang.reflect.InvocationHandler;
25 import java.lang.reflect.Method;
26
27 public class DynamicProxy implements InvocationHandler {
28     private Object mObject;
29
30     DynamicProxy(Object object) {
31         mObject = object;
32     }
33
34     @Override
35     public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable {
36         System.out.println("invoke methodName=" + method.getName());
37         method.invoke(mObject, objects);
38         return null;
39     }
40 }
41
42 //Client
43 import java.lang.reflect.InvocationHandler;
44 import java.lang.reflect.Proxy;
45
46 public class Client {
47     public static void main(String[] args) {
48         IShop boss = new Boss();
49         InvocationHandler proxyHandler = new DynamicProxy(boss);
50         ClassLoader classLoader = boss.getClass().getClassLoader();
51         IShop assitant = (IShop) Proxy.newProxyInstance(classLoader, new Class[]{IShop.class}, proxyHandler);
52         assitant.buy();
53     }
54 }

运行结果

invoke methodName=buy
I am boss, I buy buy buy
invoke methodName=drive
I am leader,I drive drive drive

由于不需要单独实现代理类,多个代理场景下实际上就节约了很多的代码量。

结语

代理模式作为设计模式中的一种,使用比较广泛,内容非常多,笔者知识有限,暂时先介绍这么多,希望能帮助初学者能够掌握代理模式的基本知识和使用。

微信扫一扫,分享到朋友圈

插件化开发笔记(一)代理模式

通过nginx反向代理实现内网yum源

上一篇

到底什么才是真正的空间复杂度?

下一篇

你也可能喜欢

插件化开发笔记(一)代理模式

长按储存图像,分享给朋友