zoukankan      html  css  js  c++  java
  • 插件化开发笔记(一)代理模式

    前言

          插件化开发所涉及到的技术点非常多,比如程序的启动流程、四大组件启动流程、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

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

    结语

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

  • 相关阅读:
    codeforces 872 D. Something with XOR Queries(思维)
    hihocoder #1609 : 数组分拆II(思维)
    hihocoder #1608 : Jerry的奶酪(状压dp)
    lightoj 1126
    SpringBoot Controller接收参数的几种常用方式
    mysql恢复备份数据时,部分表数据丢失的问题
    Tomcat证书安装(pfx和jks)
    Java 数据返回接口封装
    Java RSA 公钥加密私钥解密
    Redis连接池
  • 原文地址:https://www.cnblogs.com/andy-songwei/p/13382222.html
Copyright © 2011-2022 走看看