Version: Next

代理模式

为什么要学习代理模式?因为这是Spring AOP的底层

  • SpringAOP 必问

  • SpringMVC 必问

  • 分类

    1. 静态代理
    2. 动态代理
  • UML类图 image-20200401190614394

1. 静态代理

角色分析:

  • 抽象角色Subject:一般会使用接口或者抽象类
  • 真实角色RealSubject:被代理的角色
  • 代理角色Proxy:代理真实角色后,我们一般会做一些附属操作
  • 客户:访问代理对象,用代理对象做一些操作的人
  • 新建一个Maven模块spring-08-proxy

    • public class Landlord implements com.bsx.client.demo01.interfaces.Landlord {
      public void rent() {
      System.out.println("房东要出租房子");
      }
      }
      public class Proxy implements com.bsx.client.demo01.interfaces.Landlord {
      private Landlord landlord;
      public Proxy() {
      }
      public Proxy(Landlord landlord) {
      this.landlord = landlord;
      }
      //加一些方法,体现代理对象可以对源对象做增强
      public void rent() {
      this.landlord.rent();
      checkHouse();
      collectFee();
      }
      public void checkHouse(){
      System.out.println("中介代理带你看房");
      }
      public void collectFee(){
      System.out.println("收中介费");
      }
      }
    public class Client {
    public static void main(String[] args) {
    //为了让代理对象有东西可代理,创建一个真实房东
    //代理可以对被代理对象做增强
    Landlord landlord = new Landlord();
    Proxy proxy = new Proxy(landlord);
    proxy.rent();
    }
    }
  • 接口

    /***
    * 房东接口
    */
    public interface Landlord {
    public void rent();
    }

1.1 应用实例

  • 新建包demo02

  • 接口

    public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
    }
  • 实现类

    public class UserServiceImpl implements UserService{
    public void add() {
    System.out.println("增加了一个用户");
    }
    public void delete() {
    System.out.println("删除了一个用户");
    }
    public void update() {
    System.out.println("更新了一个用户");
    }
    public void query() {
    System.out.println("查询了一个用户");
    }
    }
  • 客户端

    public class Client {
    public static void main(String[] args) {
    UserServiceImpl userService = new UserServiceImpl();
    userService.add();
    }
    }

假如头头要求我们在方法调用前输出一条日志,那么输出日志的代码在每个方法都得添加 ↓

public class UserServiceImpl implements UserService{
public void add() {
System.out.println("日志:使用了add方法");
System.out.println("增加了一个用户");
}
public void delete() {
System.out.println("日志:使用了add方法");
System.out.println("删除了一个用户");
}
public void update() {
System.out.println("日志:使用了add方法");
System.out.println("更新了一个用户");
}
public void query() {
System.out.println("日志:使用了add方法");
System.out.println("查询了一个用户");
}
}

这显然是不好的写法,依据开闭原则,我们可以用代理在不改变源代码的情况下,扩展出日志功能

  • 代理类

    public class UserServiceProxy implements UserService {
    private UserServiceImpl userService;
    public void setUserService(UserServiceImpl userService) {
    this.userService = userService;
    }
    public void add() {
    log("add");
    userService.add();
    }
    public void delete() {
    log("delete");
    userService.delete();
    }
    public void update() {
    log("update");
    userService.update();
    }
    public void query() {
    log("query");
    userService.query();
    }
    //日志方法
    public void log(String message) {
    System.out.println("[Debug] 使用了" + message + "方法");
    }
    }
  • 客户端

    public class Client {
    public static void main(String[] args) {
    UserServiceImpl userService = new UserServiceImpl();
    UserServiceProxy userServiceProxy = new UserServiceProxy();
    userServiceProxy.setUserService(userService);
    userServiceProxy.add();
    }
    }

2. 动态代理

  • 动态代理类是动态生成的,不是直接写好的
  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
    • 基于接口:JDK动态代理
    • 基于类:cglib
    • Java字节码实现:javassist

2.1 JDK动态代理

需要了解两个东西:Proxy类、InvocationHandler接口

  • InvocationHandler接口 —— 用来指定如何增强对象
    • 方法(只有一个)
      • invoke(Object proxy, 方法 method, Object[] args)
        • proxy:你要代理谁
        • method:代理这个类里的哪一个方法
        • args:给代理方法里传的参数
  • Proxy类 (反射) —— 用来生成代理对象实例
    • Proxy.getProxyClass(Xxx.class.getClassLoader(), Xxx.class),返回代理类
    • Proxy.getInvocationHandler(Object proxy)
    • Proxy.newProxyInstance(ClassLoader loader, 类[] interfaces, InvocationHandler h)
      • interfaces:类和代理类实现的共同接口
      • invocationHandler:new 一个扔进去
  • 真实类

    /***
    * 真实房东
    */
    public class Landlord implements LandlordInterface {
    public void rent() {
    System.out.println("房东要出租房子");
    }
    }
  • 接口

    /***
    * 房东接口
    */
    public interface LandlordInterface {
    public void rent();
    }
  • 动态代理类

    public class DynamicProxyLandlord implements InvocationHandler {
    //1.真实对象
    private Landlord landlord;
    /***
    * 增强对象
    * @param proxy
    * @param method 要优化/拦截的方法
    * @param args
    * @return
    * @throws Throwable
    */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    EnhanceLogPre();
    //真实对象landlord调用方法
    Object result = method.invoke(landlord, args);
    EnhanceLogSuf();
    return result;
    }
    /***
    * 通过Proxy获取动态代理的对象
    * - 第二个参数用来实现和被代理对象一样的接口
    * - 第三个参数用来指定一个InvocationHandler,指定如何增强对象
    */
    public Object getProxyInstance() {
    return Proxy.newProxyInstance(landlord.getClass().getClassLoader(),
    landlord.getClass().getInterfaces(), this);
    }
    public void setLandlord(Landlord landlord) {
    this.landlord = landlord;
    }
    private void EnhanceLogPre() {
    System.out.println("[info] 动态代理日志,增强方法执行,前");
    }
    private void EnhanceLogSuf() {
    System.out.println("[info] 动态代理日志,增强方法执行,后");
    }
    }
  • 客户端

    public class Client {
    public static void main(String[] args) {
    //1.创建真实对象
    Landlord landlord = new Landlord();
    //2.创建用来动态生成代理类的 类的对象
    DynamicProxyLandlord dynamicProxyLandlord = new DynamicProxyLandlord();
    //3.把真实对象扔进去
    dynamicProxyLandlord.setLandlord(landlord);
    //4.获取代理对象,强转成接口
    LandlordInterface proxyLandlord =
    (LandlordInterface) dynamicProxyLandlord.getProxyInstance();
    //5.通过代理对象执行方法
    proxyLandlord.rent();
    }
    }