• 代理模式


    为什么要学习代理模式?因为这就是Spring AOP的底层机制!【Spring AOP 和 Spring MVC】

    1、什么是代理模式

    代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

    通俗的来讲代理模式就是我们生活中常见的中介

    代理模式的分类

    • 静态代理
    • 动态代理

    在学习AOP之前 , 我们要先了解代理模式

    image-20210121135942509

    真实案例:租房

    image-20210121140823935

    2、静态代理

    静态代理角色分析

    • 抽象角色:一般会使用接口或者抽象类来解决
    • 真实角色:被代理的角色
    • 代理角色:代理真实的角色,代理真实的角色后,我们一般会做一些附属操作
    • 客户:访问代理角色的人

    静态代理具体实现步骤

    1. 抽象的接口

      // 租房的接口
      public interface Rent {
          // 房屋出租的方法
          void rent();
      }
      
    2. 真实对象

      // 房东是真实的对象
      public class Landlord implements Rent {
      
          @Override
          public void rent() {
              // 房东需要出租房子
              System.out.println("房东要出租房子");
          }
      }
      
    3. 代理对象

      // 房屋中介就是代理对象
      public class Proxy implements Rent {
          // 中介可以找到房东
          private Landlord landlord;
      
          public Proxy() {
          }
      
          public Proxy(Landlord landlord) {
              this.landlord = landlord;
          }
      
          // 代理对象提供出租房屋的方法
          @Override
          public void rent() {
              // 在出租房屋时,添加一些自己的方法
              this.seeHouse();
              // 代理调用房东的出租房屋方法
              landlord.rent();
              this.fare();
          }
      
          //看房
          private void seeHouse(){
              System.out.println("带房客看房");
          }
      
          //收中介费
          private void fare(){
              System.out.println("收中介费");
          }
      }
      
    4. 客户端

      public class Client {
          public static void main(String[] args) {
              // 房东要出租房屋
              Landlord landlord = new Landlord();
              // 房屋中介帮房东出租房屋
              Proxy proxy = new Proxy(landlord);
              // 客户找到房屋中介租房
              proxy.rent();
              // 整个过程,客户不用接触房东,而是房屋中介找到房东
          }
      }
      

    分析:在这个过程中,你直接接触的就是中介,就如同现实生活中的样子,你看不到房东,但是你依旧租到了房东的房子通过代理,这就是所谓的代理模式。程序源自于生活,所以学编程的人,一般能够更加抽象的看待生活中发生的事情。

    静态代理模式的优点

    • 可以使得我们的真实角色更加纯粹!不再去关注一些公共的事情。
    • 公共的业务由代理来完成,实现了业务的分工。
    • 公共业务发生扩展时变得更加集中和方便!

    缺点

    • 一个真实角色,就需要一个代理角色;代码量会翻倍
    • 类多了,多了代理类,工作量变大了,开发效率降低!

    3、静态代理再理解

    使用代理,实现我们平时写的增删改查。

    1. 编写的一个抽象角色UserService

      // 抽象角色,增删改查
      public interface UserService {
          void insert();
          void delete();
          void update();
          void query();
      }
      
    2. 编写一个真实的对象UserServiceImpl实现UserService

      // 真实的对象,实现了增删改查方法
      public class UserServiceImpl implements UserService {
          @Override
          public void insert() {
              System.out.println("新增一个用户");
          }
      
          @Override
          public void delete() {
              System.out.println("删除一个用户");
          }
      
          @Override
          public void update() {
              System.out.println("更新一个用户");
          }
      
          @Override
          public void query() {
              System.out.println("查询一个用户");
          }
      }
      
    3. 添加一个需求,为每个方法添加一个日志功能,如何实现?

      • 思路1:为每个方法手动添加一个日志方法【太麻烦,也不符合面向对象的设计原则】
      • 思路2:添加一个代理类,在原方法不改变的情况下,帮助我们添加日志!
    4. 编写一个UserServiceProxy代理类

      // 代理对象,在这里面增加日志的实现
      public class UserServiceProxy implements UserService {
          private UserService userService;
      
          public void setUserService(UserService userService) {
              this.userService = userService;
          }
      
          @Override
          public void insert() {
              userService.insert();
              this.log("新增");
          }
      
          @Override
          public void delete() {
              userService.delete();
              this.log("删除");
          }
      
          @Override
          public void update() {
              userService.update();
              this.log("更新");
          }
      
          @Override
          public void query() {
              userService.query();
              this.log("查询");
          }
      
          // 日志功能的额外方法
          private void log(String msg) {
              System.out.println("[Debug] 这是" + msg + "的日志");
          }
      }
      
    5. 测试结果

      public class Client {
          public static void main(String[] args) {
              // 真实的对象
              UserService service = new UserServiceImpl();
              // 代理对象
              UserServiceProxy proxy = new UserServiceProxy();
              proxy.setUserService(service);
      
              // 通过代理对象实现了日志功能
              proxy.insert();
              proxy.delete();
              proxy.update();
              proxy.query();
          }
      }
      

    通过上面的例子,我们已经基本了解了静态代理模式,就是:

    我们在不改变原来的代码的情况下,实现了对原有功能的增强,这是AOP中最核心的思想

    AOP:纵向开发,横向开发

    image-20210121151619785

    4、动态代理

    了解完静态代理后,你会发现每个代理类只能为一个接口服务,这样程序开发中必然会产生许多的代理类

    所以我们就会想办法可以通过一个代理类完成全部的代理功能,那么我们就需要用动态代理

    动态代理的概念

    • 动态代理的角色和静态代理的一样
    • 动态代理的代理类是动态生成的,而静态代理的代理类是我们提前写好的。
    • 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理
      • 基于接口的动态代理----JDK动态代理
      • 基于的动态代理--cglib
      • 现在用的比较多的是java字节码javasist来生成动态代理,可以百度一下javasist
      • 我们这里使用JDK的原生代码来实现,其余的道理都是一样的!

    JDK的动态代理需要了解两个类

    核心:InvocationHandlerProxy, 可以打开JDK帮助文档看看

    【InvocationHandler:调用处理程序】

    image-20210121155114930

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
    // proxy - 调用该方法的代理实例
    // method -所述方法对应于调用代理实例上的接口方法的实例。方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。
    // args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean 。
    

    【Proxy : 代理】

    image-20210121155403929

    image-20210121155436050

    image-20210121155539944

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

    具体代码实现:房屋出租

    1. 抽象的接口

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

      public class Host implements Rent {
          @Override
          public void rent() {
              System.out.println("出租房屋");
          }
      }
      
    3. 代理的角色

      public class ProxyInvocationHandler implements InvocationHandler {
          // 被代理的接口
          private Object target;
      
          public void setRent(Object target) {
              this.target = target;
          }
      
          // proxy: 代理类。
          // method: 代理类的调用处理程序的方法对象。
          // 处理代理实例上的方法调用并返回结果
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              // 核心:本质利用反射实现!
              Object invoke = method.invoke(target, args);
              return invoke;
          }
          // 生成代理类,重点是第二个参数,获取要代理的抽象角色!之前都是一个角色,现在可以代理一类角色
          public Object getProxy() {
              return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
          }
      }
      
    4. 客户

      public class Client {
          public static void main(String[] args) {
              // 真实的角色
              Rent rent = new Host();
              // 代理实例的调用处理程序
              ProxyInvocationHandler handler = new ProxyInvocationHandler();
              // 将真实角色放置进去!
              handler.setRent(rent);
              // 动态生成对应的代理类!
              Rent proxy = (Rent) handler.getProxy();
              proxy.rent();
          }
      }
      

    5、动态代理再理解

    使用动态代理的方式实现增删改查

    因为我们自己编写的ProxyInvocationHandler.java是一个通用的代理类

    我们使用它实现代理之前的UserService就非常简单!

    1. 抽象的对象

      // 抽象角色,增删改查
      public interface UserService {
          void insert();
          void delete();
          void update();
          void query();
      }
      
    2. 真实的对象

      public class UserServiceImpl implements UserService {
          @Override
          public void insert() {
              System.out.println("新增一个用户");
          }
      
          @Override
          public void delete() {
              System.out.println("删除一个用户");
          }
      
          @Override
          public void update() {
              System.out.println("更新一个用户");
          }
      
          @Override
          public void query() {
              System.out.println("查询一个用户");
          }
      }
      
    3. 代理的对象

      public class UserServiceProxy implements InvocationHandler {
          private Object target;
      
          public void setTarget(Object target) {
              this.target = target;
          }
      
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              Object invoke = method.invoke(target, args);
              // 执行后设置 log
              this.log(method.getName());
              return invoke;
          }
          
          public Object getProxy() {
              return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
          }
          // 添加一个log方法
          private void log(String msg) {
              System.out.println("[Debug] 这是" + msg + "的日志");
          }
      }
      
    4. 客户

      public class Client {
          public static void main(String[] args) {
              // 真实的对象
              UserService service = new UserServiceImpl();
              // 代理对象
              UserServiceProxy userServiceProxy = new UserServiceProxy();
              userServiceProxy.setTarget(service);
              UserService proxy = (UserService) userServiceProxy.getProxy();
              // 通过代理对象实现了日志功能
              proxy.insert();
              proxy.delete();
              proxy.update();
              proxy.query();
          }
      }
      

    我们可以发现,代理的对象类基本不用改变,只是添加了我们自己的log方法,就可以代理房屋出租,也可以代理UserService。这样我们只需要一个代理对象,就可以代理多个对象

    动态代理的好处

    静态代理有的它都有,静态代理没有的,它也有!

    • 可以使得我们的真实角色更加纯粹,不再去关注一些公共的事情。
    • 公共的业务由代理来完成,实现了业务的分工。
    • 公共业务发生扩展时变得更加集中和方便。
    • 一个动态代理,一般代理某一类业务。
    • 一个动态代理可以代理多个类,代理的是接口!
    懂不懂,都是收获
  • 相关阅读:
    Python-深浅拷贝
    Python-生成式
    Python-手写web应用
    Python-为什么产生了GIL锁?
    Python-文件处理
    Python-线程
    10大网站设计错误 足以毁掉你的网站【转】
    [转]ASP.NET验证发生前无法调用 Page.IsValid。应在 CausesValidation=True 且已启动回发的控件
    jquery操作字符串常用方法总结及工作代码
    C#中的序列化和反序列化是什么、有什么作用、使用方法详解
  • 原文地址:https://www.cnblogs.com/paidaxing0623/p/14312602.html
Copyright © 2020-2023  润新知