插件化开发笔记(一)代理模式
前言
插件化开发所涉及到的技术点非常多,比如程序的启动流程、四大组件启动流程、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
由于不需要单独实现代理类,多个代理场景下实际上就节约了很多的代码量。
结语
代理模式作为设计模式中的一种,使用比较广泛,内容非常多,笔者知识有限,暂时先介绍这么多,希望能帮助初学者能够掌握代理模式的基本知识和使用。