zoukankan      html  css  js  c++  java
  • 代理模式笔记

    image-20200531213054908

    学习资源来自于哔哩哔哩UP遇见狂神说,一个宝藏UP大家快去关注吧!记得三连加分享,不要做白嫖党.

    由于使用的是国外的imgur图床,图片无法显示请魔法上网 !

    由于使用的是国外的imgur图床,图片无法显示请魔法上网 !

    由于使用的是国外的imgur图床,图片无法显示请魔法上网 !

    10. 代理模式

    为什么要学代理模式 ? 因为这就是SpringAOP的底层 ! [面试必问: SpringAOP 和 SpringMVC]

    代理模式的分类 :

    • 静态代理
    • 动态代理

    Imgur

    10.1 静态代理

    角色分析 :

    • 抽象角色 : 共同完成的事情(租房) , 一般会用接口或者抽象类来解决
    • 真实角色 : 被代理的角色(房东)
    • 代理角色 : 代理真实角色(中介) , 代理真实角色后,我们一般会做一些附属操作
    • 客户 : 访问代理对象的人(你)

    Imgur

    代码步骤 :

    1. 接口

      //租房
      public interface Rent {
          public void rent();
      }
      
    2. 真实角色

      //房东
      public class Landlord implements Rent {
          public void rent() {
              System.out.println("房东出租了房屋!");
          }
      }
      
    3. 代理角色

      //代理角色
      public class Proxy implements Rent{
      
          private Landlord landlord;
      
          public Proxy() {
          }
      
          public Proxy(Landlord landlord) {
              this.landlord = landlord;
          }
      
          public void rent() {
              landlord.rent();
          }
      
          //看房
          public void seeHouse() {
              System.out.println("中介带你看房");
          }
      
          //收中介费
          public void fare() {
              System.out.println("收中介费");
          }
      
          //签合同
          public void hetong() {
              System.out.println("签租赁合同");
          }
      }
      
    4. 客户端访问代理角色

      public class Client {
          public static void main(String[] args) {
              //房东要租房子
              Proxy proxy = new Proxy(new Landlord());
              //代理,中介帮房东租房子,但是代理一般会有一些附属操作!
              proxy.hetong();
              proxy.fare();
              proxy.seeHouse();
      
              //你不用面对房东,直接找中介租房即可!
              proxy.rent();
          }
      }
      

    代理模式和好处 :

    • 可以使真实角色的操作更加纯粹! 不用去关注一些共业务
    • 公共业务就交给代理角色! 实现业务的分工 !
    • 公共业务发生扩展的时候,方便集中管理 !

    缺点 :

    • 一个真实角色就会产生一个代理角色 , 代码量会翻倍 , 开发效率变低 .

    10.2 加深理解

    代码 : 1.以业务层添加日志为例子 :

    Imgur

    1. 增删改查接口 :

    Imgur

    1. 业务层实现

    Imgur

    1. 添加一个代理类,并增加一个日志方法,这样可以不改动原有代码 .

    Imgur

    1. 测试 :

    横向开发导图 :

    Imgur

    10.3 动态代理

    • 动态代理和静态代理一样
    • 动态代理是动态生成的,不是我们写好的
    • 动态代理可分为两大类 : 基于接口的动态代理 , 基于类的动态代理
      • 基于接口 : JDK 动态代理 [例子中使用这个]
      • 基于类 : cglib
      • java字节码实现 : JAVAssist

    需要了解两个类 : Proxy : 代理, InvocationHandler : 调用处理程序

    • Proxy提供了创建动态代理类的静态方法
    • InvocationHandler 是由代理实例的湖用处理程实现的接口。
      每个代理实例都有一个关联的调用处理程序。当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法。

    动态代理代码总览 :

    Imgur

    动态代理的核心 --->工具类: ProxyInvocationHandler.java

    //等下我们会用这个类,自动生成代理类!
    public class ProxyInvocationHandler implements InvocationHandler {
    
        //被代理的接口
        private Object target;
    
        public void setTarget(Object target) {
            this.target = target;
        }
    
        //生成得到被代理类
        public Object getProxy() {
            return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                    target.getClass().getInterfaces(), this);
        }
    
    
        //处理代理实例,并返回结果
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //动态代理的本质,就是使用反射机制实现
            log(method.getName());
            return method.invoke(target, args);
        }
    
        public void log(String msg) {
            System.out.println("执行了" + msg + "方法");
        }
    }
    

    10.4 自己的理解

    静态代理模式代码思路 :

    接口 ---> 接口实现类 ---> 代理类 ---> 测试类

    • 接口中写要实现的方法

    • 接口实现类的作用十分纯粹 , 就是实现接口中的方法

    • 在代理类中将接口实现类作为一个私有属性,并通过set方法传参,实现代理

      private UserServiceImpl userService;
      
      public void setUserService(UserServiceImpl userService) {
          this.userService = userService;
      }
      
    • 在测试类中new出代理类,代理类通过set方法,将new出来的接口实现类对象作为参数传参,就可以开始调用方法了

      UserServiceProxy proxy = new UserServiceProxy();
      proxy.setUserService(new UserServiceImpl());
      

    动态代理模式实现思路 :

    • 重点部分就是将将接口实现类作为一个私有属性,并通过set方法传参 , 动态代理将这个set方法传的具体参数类变为了一个可变的参数类. Object target代表一个未知的参数类(接口实现类).
    //被代理的接口
    private Object target;
    
    public void setTarget(Object target) {
        this.target = target;
    }
    
    • 既然这个参数类未知,那么我就得将它生成出来,通过反射来实现

      //生成得到被代理类
      public Object getProxy() {
          return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                  target.getClass().getInterfaces(), this);
      }
      

      这是方法是一个固定写法,三个参数分别为:
      1.得到类加载器 2.得到代理的类的接口 3. this代表InvocationHandler .
      因为动态代理的工具类需要实现InvocationHandler接口,所以直接写 this

    • 现在已经通过反射获取了代理的接口,剩下的就是一个执行方法

      //处理代理实例,并返回结果
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          //动态代理的本质,就是使用反射机制实现
          return method.invoke(target, args);
      }
      

      这是继承InvocationHandler接口所需要实现的方法, 它的作用就是通过反射自动运行接口方法(它的实现是通过反射找到接口的接口实现类,并运行接口实现类的方法).

    • 如果要拓展方法就可以直接在工具类中加.

    动态代理的完整工具类 :

    //等下我们会用这个类,自动生成代理类!
    public class ProxyInvocationHandler implements InvocationHandler {
    
        //被代理的接口
        private Object target;
    
        public void setTarget(Object target) {
            this.target = target;
        }
    
        //生成得到被代理类
        public Object getProxy() {
            return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                    target.getClass().getInterfaces(), this);
        }
    
    
        //处理代理实例,并返回结果
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //动态代理的本质,就是使用反射机制实现
            log(method.getName());
            return method.invoke(target, args);
        }
    
        //添加拓展方法
        public void log(String msg) {
            System.out.println("执行了" + msg + "方法");
        }
    }
    

    测试类:

    public class Client {
        public static void main(String[] args) {
            //真实角色
            UserServiceImpl userService = new UserServiceImpl();
            //被代理角色,不存在
            ProxyInvocationHandler pih = new ProxyInvocationHandler();
    
            pih.setTarget(userService); //设置要代理的对象
            
            //动态生成代理类
            UserService proxy = (UserService) pih.getProxy();
    
            proxy.add();
            proxy.delete();
            proxy.update();
            proxy.query();
        }
    }
    

    简化一下:

    public class Client {
        public static void main(String[] args) {
    
            ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
            proxyInvocationHandler.setTarget(new UserServiceImpl());
            UserService proxy = (UserService) proxyInvocationHandler.getProxy();
            
            proxy.add();
            proxy.delete();
            proxy.update();
            proxy.query();
        }
    }
    

    实例化真实角色(接口实现类) ---> 实例化接口实现类 ---> 用接口实现类的set方法注入(代理)真实角色 ---> 用接口实现类的getProxy方法生成代理类 ---> 运行代理类的方法和拓展方法 .

  • 相关阅读:
    tomcat配置通过域名直接访问项目首页步骤
    kafka配置参数
    nginx平滑升级
    redsi一主两从三哨兵
    kill
    lelnet爱一直在
    在linux中查看进程占用的端口号
    监控redis
    老猿学5G随笔:RAN、RAT以及anchor移动性锚点的概念
    老猿学5G随笔:5G网元功能体NF以及NF之间的两种接口--服务化接口和参考点
  • 原文地址:https://www.cnblogs.com/tanshishi/p/13149850.html
Copyright © 2011-2022 走看看