• 第三节:代理模式——动态代理


    一、动态代理模式的基本介绍

      1、代理对象,不需要实现接口,但是目标对象要实现接口,否则不能动态代理;

      2、代理对象的生成,是利用 JDK 的 API,动态的在内存中构建代理对象;

      3、动态代理也叫做:JDK 代理、接口代理

      4、实现步骤

        (1)代理对象和真实对象实现相同的接口;

        (2)代理对象 = Proxy.newProxyInstance();

        (3)使用代理对象调用方法

        (4)增强方法

          增强方法方式:

            ① 增强参数列表

            ② 增强返回值类型

            ③ 增强方法体执行逻辑

    二、JDK 中生成代理对象的 API

      1、代理类所在包:java.lang.reflect.Proxy

      2、JDK 实现代理只需要使用 newProxyInstance 方法,但是该方法需要接受三个参数,完整的写法是:

    1  public static Object newProxyInstance(ClassLoader loader,
    2                                           Class<?>[] interfaces,
    3                                           InvocationHandler h)

    三、动态代理应用实例

      将上一节的静态代理改进成动态代理模(即:JDK代理模式

      1、UML 类图

        

      2、代码实现:

        接口:

    1 /**
    2  * 接口
    3  */
    4 public interface ITeacherDao {
    5     void teach(); //授课
    6 
    7     void sayHello(String name);
    8 }

        被代理对象(目标对象):

     1 public class TeacherDao implements ITeacherDao{
     2     @Override
     3     public void sayHello(String name) {
     4         System.out.println("Hello," + name);
     5     }
     6 
     7     @Override
     8     public void teach() {
     9         System.out.println("老师授课中。。。");
    10     }
    11 }

        代理工厂:

     1 public class ProxyFactory {
     2 
     3     //维护一个目标对象,Object
     4     private Object target;
     5 
     6     /**
     7      * 通过构造方法,对 target 进行初始化
     8      */
     9     public ProxyFactory(Object target) {
    10         this.target = target;
    11     }
    12 
    13     /**
    14      *  给目标对象 生成一个代理对象
    15      * @return
    16      */
    17     public Object getProxyInstance() {
    18         /**
    19          * public static Object newProxyInstance(ClassLoader loader,
    20          *                                     Class<?>[] interfaces,
    21          *                                     InvocationHandler h)
    22          * 参数说明:
    23          *  1、ClassLoader loader:
    24          *      指定当前目标对象使用的类加载器,获取加载器的方法固定的
    25          *  2、Class<?>[] interfaces
    26          *      目标对象(被代理对象)实现的接口类型,使用泛型方式确认类型
    27          *  3、InvocationHandler h:
    28          *      事件处理,执行目标对象的方法时,会触发事件处理器方法,
    29          *      会把当前执行的目标对象方法作为一个参数传入
    30          */
    31         return Proxy.newProxyInstance(target.getClass().getClassLoader(),
    32                 target.getClass().getInterfaces(),
    33                 new InvocationHandler() {
    34 
    35                     @Override
    36                     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    37                         System.out.println("JDK代理开始");
    38                         //通过反射机制调用目标对象的方法
    39                         Object invoke = method.invoke(target, args);
    40                         System.out.println("JDK代理提交");
    41                         return invoke;
    42                     }
    43                 });
    44     }
    45 }

        客户端:

     1 public class Client {
     2     public static void main(String[] args) {
     3         //创建一个目标对象
     4         ITeacherDao target = new TeacherDao();
     5 
     6         //给目标对象创建代理对象,可以转成 ITeacherDAO
     7         ProxyFactory proxyFactory = new ProxyFactory(target);
     8         ITeacherDao proxyInstance = (ITeacherDao)proxyFactory.getProxyInstance();
     9 
    10         System.out.println("proxyInstance=" + proxyInstance);
    11         //proxyInstance类型=class com.sun.proxy.$Proxy0 内存中动态生成了代理对象
    12         System.out.println("proxyInstance类型=" + proxyInstance.getClass());
    13 
    14         //通过代理对象,调用目标对象的方法
    15         proxyInstance.teach();
    16 
    17         proxyInstance.sayHello("Tom");
    18     }
    19 }

    四、动态代理应用实例

      我们用买电脑这件事情来叙述一下动态代理:

      当我们想买一个联想电脑,而此时联想电脑公司在北京,我们在西安,如果买电脑还得去北京,比较浪费时间。

      这是在西安有一个联想代理商,我们可以从代理商这里预订一台电脑,而代理商去北京给我们取货。这样就形成了一种动态代理。

      

      在这里:

        北京联想公司=真实对象

        西安联想代理商=代理对象

      这里西安代理商可以把联想电脑销售到西安各地,扩大了销售市场,相当于增强了功能。

      代码演示

      卖电脑的接口类:

    1 public interface SaleComputer {
    2 
    3     public String sale(double money);
    4 
    5     public void show();
    6 }

      卖电脑真实类:

     1 /**
     2  * 真实类
     3  */
     4 public class Lenovo implements SaleComputer {
     5     @Override
     6     public String sale(double money) {
     7 
     8         System.out.println("花了"+money+"元买了一台联想电脑...");
     9         return "联想电脑";
    10     }
    11 
    12     @Override
    13     public void show() {
    14         System.out.println("展示电脑....");
    15     }
    16 }

      代理对象类

     1 import java.lang.reflect.InvocationHandler;
     2 import java.lang.reflect.Method;
     3 import java.lang.reflect.Proxy;
     4 
     5 public class ProxyTest {
     6 
     7     public static void main(String[] args) {
     8         //1.创建真实对象
     9         Lenovo lenovo = new Lenovo();
    10         
    11         //2.动态代理增强lenovo对象
    12         /*
    13             三个参数:
    14                 1. 类加载器:真实对象.getClass().getClassLoader()
    15                 2. 接口数组:真实对象.getClass().getInterfaces()
    16                 3. 处理器:new InvocationHandler()
    17          */
    18         SaleComputer proxy_lenovo = (SaleComputer) Proxy.newProxyInstance(lenovo.getClass().getClassLoader(), lenovo.getClass().getInterfaces(), new InvocationHandler() {
    19 
    20             /*
    21                 代理逻辑编写的方法:代理对象调用的所有方法都会触发该方法执行
    22                     参数:
    23                         1. proxy:代理对象
    24                         2. method:代理对象调用的方法,被封装为的对象
    25                         3. args:代理对象调用的方法时,传递的实际参数
    26              */
    27             @Override
    28             public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    29                 /*System.out.println("该方法执行了....");
    30                 System.out.println(method.getName());
    31                 System.out.println(args[0]);
    32 
    33 
    34 
    35 
    36 */
    37                 //判断是否是sale方法
    38                 if(method.getName().equals("sale")){
    39                     //1.增强参数
    40                     double money = (double) args[0];
    41                     money = money * 0.85;
    42                     System.out.println("专车接你....");
    43                     //使用真实对象调用该方法
    44                     String obj = (String) method.invoke(lenovo, money);
    45                     System.out.println("免费送货...");
    46                     //2.增强返回值
    47                     return obj+"_鼠标垫";
    48                 }else{
    49                     Object obj = method.invoke(lenovo, args);
    50                     return obj;
    51                 }
    52 
    53 
    54 
    55             }
    56         });
    57 
    58         //3.调用方法
    59 
    60         String computer = proxy_lenovo.sale(8000);
    61         System.out.println(computer);
    62 
    63         proxy_lenovo.show();
    64     }
    65 }
  • 相关阅读:
    PHP 将二维数组中某列值作为数组的键名
    MySQL 8下忘密码后重置密码
    单一职责原则
    Linux下安装SVN服务端小白教程
    go 代码玩耍
    centos7 docker开启认证的远程端口2376配置教程
    Dockerfile RUN,CMD,ENTRYPOINT命令区别
    wait-for-it.sh脚本控制docker-compose启动顺序详解
    阿里云服务器漏洞修复_2020.5.22
    Let's Encrypt 免费通配符 SSL 证书申请教程
  • 原文地址:https://www.cnblogs.com/niujifei/p/14364348.html
Copyright © 2020-2023  润新知