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

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

    结语

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

  • 相关阅读:
    104.Maximum Depth of Binary Tree
    103.Binary Tree Zigzag Level Order Traversal
    102.Binary Tree Level Order Traversal
    101.Symmetric Tree
    100.Same Tree
    99.Recover Binary Search Tree
    98.Validate Binary Search Tree
    97.Interleaving String
    static静态初始化块
    serialVersionUID作用
  • 原文地址:https://www.cnblogs.com/andy-songwei/p/13382222.html
Copyright © 2011-2022 走看看