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

    为什么要学习代理模式?因为这就是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。这样我们只需要一个代理对象,就可以代理多个对象

    动态代理的好处

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

    • 可以使得我们的真实角色更加纯粹,不再去关注一些公共的事情。
    • 公共的业务由代理来完成,实现了业务的分工。
    • 公共业务发生扩展时变得更加集中和方便。
    • 一个动态代理,一般代理某一类业务。
    • 一个动态代理可以代理多个类,代理的是接口!
    懂不懂,都是收获
  • 相关阅读:
    149. Max Points on a Line(js)
    148. Sort List(js)
    147. Insertion Sort List(js)
    146. LRU Cache(js)
    145. Binary Tree Postorder Traversal(js)
    144. Binary Tree Preorder Traversal(js)
    143. Reorder List(js)
    142. Linked List Cycle II(js)
    141. Linked List Cycle(js)
    140. Word Break II(js)
  • 原文地址:https://www.cnblogs.com/paidaxing0623/p/14312602.html
Copyright © 2011-2022 走看看