java代理实现

一、代理模式

给某个对象提供一个代理对象,并由代理对象控制对于原对象的访问.

按照代理对象的创建时期,可分为静态代理和动态代理。
静态代理:静态代理在编译时就已经实现,编译完成后代理类是一个实际的class文件
动态代理:动态代理是在运行时动态生成的,即编译完成后没有实际的class文件,而是在运行时动态生成类字节码,并加载到JVM中

二、静态代理

举例:我想卖车,但我很忙,不想被电话骚扰,于是交给二手车交易所来帮我卖车

卖车子接口

interface SaleCar{
  void sale();
}

张三真正卖车子的实现类(委托类

class ZhangSanTrade implements SaleCar{
  @Override
  public void sale() {
    System.out.println("zhangsan sale his car");
  }
}

二手车交易类(代理类

class CarTradeProxy implements SaleCar{

  private ZhangSanTrade owner;

  public CarTradeProxy(ZhangSanTrade owner){
    this.owner=owner;
  }

  @Override
  public void sale() {
    System.out.println("proxy add price");
    owner.sale();
  }
}

测试
java代理实现

疑问:代理类和委托类差别不大,直接创建委托类调用sale方法不就可以了吗?
解答:代理类在真正调用委托类的方法之前做了中间加价的操作。即代理模式实现在委托类的基础上增加了额外的逻辑操作

需求增加:
我想用卖车的钱加自己的一些存款买个房子,自己也不想东奔西跑,于是把买房委托房产中介

在定义一个买房的接口

interface BuyHouse{
  void buy();
}

重写委托类,实现卖车和买房两个接口

class ZhangSanTrade implements SaleCar,BuyHouse{
  @Override
  public void sale() {
    System.out.println("zhangsan sale his car");
  }

  @Override
  public void buy() {
    System.out.println("zhangsan buy house");
  }
}

可以看到,我现在既要卖车,也要买房子
在创建一个买房子的中介代理类

class HouseTradeProxy implements BuyHouse{

  private ZhangSanTrade owner;

  public HouseTradeProxy(ZhangSanTrade owner){
    this.owner=owner;
  }

  @Override
  public void buy() {
    System.out.println("proxy add price");
    owner.buy();
  }
}

测试类

  public static void main(String[] args) {
    //委托类
    ZhangSanTrade zhangSanSaleCar=new ZhangSanTrade();
    //代理类
    CarTradeProxy carTradeProxy=new CarTradeProxy(zhangSanSaleCar);
    carTradeProxy.sale();
    System.out.println("------------------------------");
    HouseTradeProxy houseTradeProxy=new HouseTradeProxy(zhangSanSaleCar);
    houseTradeProxy.buy();
  }

优点:可以在不修改目标对象的前提下扩展目标对象的功能。
缺点:

  • 冗余。由于代理对象要实现与目标对象一致的接口,会产生过多的代理类。
  • 不易维护。一旦接口增加方法,目标对象与代理对象都要进行修改。

通过静态代理的方式,可以完美解决我们的问题,但当越来越多的委托类需要代理,而且代理做的工作又一样,会多出很多的代理类。此时想,我们可以只做一次,代理一类委托类,此时动态代理应运而生,它可以只定义一次就能为一类委托类做代理

三、动态代理

动态代理常见JDK 动态代理与 CGLIB 动态代理

区别:

  • Jdk动态代理:利用拦截器(必须实现InvocationHandler接口)加上反射机制生成一个代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理
  • Cglib动态代理:利用ASM框架,对代理对象类生成的class文件加载进来,通过修改其字节码生成子类来进行代理
    因此:
    如果想要实现JDK动态代理那么代理类必须实现接口,否则不能使用;
    如果想要使用CGlib动态代理,那么代理类不能使用final修饰类和方法;

使用场景:
AOP 的实现原理、RPC远程调用、Java 注解对象获取、日志框架、全局性异常处理、事务处理

1 JDK动态代理实现

UserService接口

public interface UserService {
  void addUser();
  void updateUser(String str);
}

UserServiceImpl实现类

public class UserServiceImpl implements UserService{

  @Override
  public void addUser() {
    System.out.println("添加用户");
  }

  @Override
  public void updateUser(String str) {
    System.out.println("更新用户信息" + str);
  }
}

UserProxy代理类,实现InvocationHandler接口重写invoke方法

public class UserProxy implements InvocationHandler {

  private Object target;

  public UserProxy(Object target) {
    this.target = target;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

    Object res = method.invoke(target, args);

    System.out.println("记录日志");

    return res;
  }
}

测试 ,实现了增强,打印出了日志
java代理实现

Proxy.newProxyInstance 方法得到的也是 UserService 的实现类对象,那么其实这是一种基于接口的动态代理。也叫做 JDK 动态代理

2 CGlib动态代理

JDK 动态代理是基于接口的代理,而 CGLIB 动态代理是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法 ,也就是说 CGLIB 动态代理采用类继承 -> 方法重写的方式进行的,下面我们先来看一下 CGLIB 动态代理的结构。

java代理实现
如上图所示,代理类继承于目标类,每次调用代理类的方法都会在拦截器中进行拦截,拦截器中再会调用目标类的方法。

CGlib需要导入Jar包,那么我用SpringBoot直接导入依赖

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

UserServiceImpl被代理类

public class UserServiceImpl implements UserService{

  @Override
  public void addUser() {
    System.out.println("添加用户");
  }

  @Override
  public void updateUser(String str) {
    System.out.println("更新用户信息" + str);
  }
}

UserServiceCGlib代理

public class UserServiceCGlib implements MethodInterceptor {

  private Object target;

  public UserServiceCGlib() {
  }

  public UserServiceCGlib(Object target) {
    this.target = target;
  }

  //返回一个代理对象:    是 target对象的代理对象
  public Object getProxyInstance() {
    //1. 创建一个工具类
    Enhancer enhancer = new Enhancer();
    //2. 设置父类
    enhancer.setSuperclass(target.getClass());
    //3. 设置回调函数
    enhancer.setCallback(this);
    //4. 创建子类对象,即代理对象
    return enhancer.create();
  }


  @Override
  public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
      throws Throwable {
    System.out.println("增强开始~~~");
    Object result = methodProxy.invokeSuper(o, objects);
    System.out.println("增强结束~~~");
    return result;
  }
}

测试类
java代理实现
注:代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理

总结:

  1. 如果加入容器的目标对象有实现接口,用JDK代理
  2. 如果目标对象没有实现接口,用Cglib代理
  3. 如果目标对象实现了接口,且强制使用cglib代理,则会使用cglib代理。
上一篇:故障分析:网络闪断引发的ServiceStack.Redis死锁问题


下一篇:多线程基础-08