What

代理模式(Proxy Design Pattern),旨在不改变原始类(或叫被代理类)代码的情况下,通过引入代理类来给原始类附加功能。

场景

  1. 业务系统的非功能性需求开发

代理模式最常用的一个应用场景就是,在业务系统中开发一些非功能性需求,比如:监控、统计、鉴权、限流、事务、幂等、日志。我们将这些附加功能与业务功能解耦,放到代理类中统一处理,让程序员只需要关注业务方面的开发。
如果你熟悉 Java 语言和 Spring 开发框架,这部分工作都是可以在 Spring AOP 切面中完成的。前面我们也提到,Spring AOP 底层的实现原理就是基于动态代理。

  1. 在 RPC中的应用

实际上,RPC 框架也可以看作一种代理模式,GoF 的《设计模式》一书中把它称作远程代理。通过远程代理,将网络通信、数据编解码等细节隐藏起来。客户端在使用 RPC 服务的时候,就像使用本地函数一样,无需了解跟服务器交互的细节。除此之外,RPC 服务的开发者也只需要开发业务逻辑,就像开发本地使用的函数一样,不需要关注跟客户端的交互细节。

  1. 在缓存中的应用

假设我们要开发一个接口请求的缓存功能,对于某些接口请求,如果入参相同,在设定的过期时间内,直接返回缓存结果,而不用重新进行逻辑处理。

How

静态代理

适合被代理类很少,且增强逻辑并不普适的场景。比如:不能改变源码的前提下,做一些具体业务逻辑的增强。

  • 静态代理存在的问题
    如果有50个被代理类需要添加相同的增强(比如记录记录执行时间),那我们就要创建 50 个对应的代理类。这会导致项目中类的个数成倍增加,增加了代码维护成本。并且,每个代理类中的代码都有点像模板式的“重复”代码,也增加了不必要的开发成本。

通过接口实现

代理模式🌟 - 图1

  • 场景
    所有自己实现的代理类,首选通过接口的方式实现。
  • 步骤
  1. 代理类和原始类实现相同的接口;
  2. 原始类只负责业务逻辑,代理类负责业务代码前后附加其他非业务逻辑;
  3. 代理类通过委托的方式调用原始类来执行业务代码。 ```java /**
    • 接口式 *
    • @author abley
    • @date 2022/1/5 10:12 */ public class InterfaceWay { public static void main(String[] args) { CircleProxy circleProxy = new CircleProxy(new Circle()); circleProxy.draw(); } }

/**

  • 图形接口类 / interface Shape { /*
    • 画图接口 */ void draw(); }

/**

  • 圆形 */ class Circle implements Shape {

    @Override public void draw() {

    1. System.out.println("圆形绘制中...");

    } }

/**

  • 圆形代理类 */ class CircleProxy implements Shape { private Circle circle;

    public CircleProxy() {

     this.circle = new Circle();
    

    }

    @Override public void draw() {

     System.out.println("准备开始画圆形:");
     circle.draw();
     System.out.println("圆形画图完毕!");
    

    } } ```

通过继承实现

代理模式🌟 - 图2

  • 场景
    如果原始类并没有定义接口,并且原始类代码并不是我们开发维护的,对于这种外部类的扩展,我们一般都是采用继承的方式。
  • 步骤
  1. 代理类继承原始类;
  2. 原始类只复杂业务逻辑,代理类负责业务代码前后附加其他非业务逻辑;
  3. 代理类通过直接调用父类(原始类)的方式来执行业务代码。 ```java /**
    • 继承式 *
    • @author abley
    • @date 2022/1/5 10:13 */ public class ExtendsWay { public static void main(String[] args) { new TriangleProxy().draw(); } }

/**

  • 三角形 */ class Triangle {

    public void draw() {

     System.out.println("三角形形绘制中...");
    

    } }

/**

  • 三角形代理类 */ class TriangleProxy extends Triangle {

    @Override public void draw() {

     System.out.println("准备绘制三角形:");
     super.draw();
     System.out.println("三角形绘制完成!");
    

    } }

<a name="V4Er7"></a>
## 动态代理
> **动态代理**(Dynamic Proxy),就是我们不事先为每个原始类编写代理类,而是在运行的时候,动态地创建原始类对应的代理类,然后在系统中用代理类替换掉原始类。因此,动态代理可以解决静态代理存在的问题。

- **优点:**

动态代理中所说的“动态”,是针对使用Java代码实际编写了代理类的“静态”代理而言的,它的真正优势不在于省去了编写代理类那一点编码工作量,而是**实现了可以在原始类和接口还未知的时候,就确定代理类的代理行为,当代理类与原始类脱离直接联系后,就可以很灵活地重用于不同的应用场景之中**。

在 jdk6、jdk7、jdk8 逐步对 JDK 动态代理优化之后,在调用次数较少的情况下,JDK 代理效率高于 CGLIB 代理效率,只有当进行大量调用的时候,jdk6 和 jdk7 比 CGLIB 代理效率低一点,但是到 jdk8 的时候,jdk 代理效率高于 CGLIB 代理。

<a name="xsmD8"></a>
### 基于Java反射实现
> 利用**反射机制**,动态生成一个代理接口的匿名类,通过InvokeHandler 来代理替换调用。

注意:因为JDK动态代理是采用的接口式的,所以如果想要实现 JDK 动态代理那么代理类必须实现接口,否则不能使用。<br />**HOW**
```java
/**
 * JDK动态代理
 * <p>
 * 利用拦截器(必须实现 InvocationHandler 接口)加上反射机制生成一个代理接口的匿名类,在调用具体方法前调用 InvokeHandler 来处理。
 * 注意:因为JDK动态代理是采用的接口式的,所以如果想要实现 JDK 动态代理那么代理类必须实现接口,否则不能使用。
 *
 * @author abley
 * @date 2022/1/5 10:48
 */
public class JdkDynamicProxyWay {

    @SneakyThrows
    public static void main(String[] args) {
        ILanguage chinese = new TimeProxy<>(new Chinese()).bind();
        //实际会调用TimeProxy#invoke
        chinese.sayHi();

        ILanguage english = new TimeProxy<>(new English()).bind();
        english.sayHello();

        /**
         * 外部创建方式
         */
        Chinese chinese1 = new Chinese();
        //创建一个InvocationHandler对象
        InvocationHandler handler = new TimeProxy<>(chinese1);
        //使用Proxy生成一个动态代理类
        Class<?> proxyClass = Proxy.getProxyClass(chinese1.getClass().getClassLoader(), ILanguage.class);
        //获取动态代理类中带一个InvocationHandler参数的构造器
        Constructor<?> ctor = proxyClass.getConstructor(InvocationHandler.class);
        //通过ctor构造对象来创建动态实例
        ILanguage chinese2 = (ILanguage) ctor.newInstance(handler);
        chinese2.sayHello();
    }
}

/**
 * 语言接口
 */
interface ILanguage {
    void sayHello();

    void sayHi();
}

/**
 * 中文
 */
class Chinese implements ILanguage {

    @Override
    public void sayHello() {
        System.out.println("哈喽!");
    }

    @Override
    public void sayHi() {
        System.out.println("嗨!");
    }

    public String to() {
        return "ChinaHello#toString";
    }
}

class English implements ILanguage {

    @Override
    public void sayHello() {
        System.out.println("hello!");
    }

    @Override
    public void sayHi() {
        System.out.println("hi!");
    }
}

/**
 * 时间代理类
 * 记录被代理函数的执行时间
 */
class TimeProxy<T> implements InvocationHandler {

    /**
     * 被代理对象
     */
    T originalObj;

    /**
     * 构造
     *
     * @param originalObj 被代理对象
     */
    public TimeProxy(T originalObj) {
        this.originalObj = originalObj;
    }

    /**
     * 绑定代理类
     * 该代理类同样实现了原始类所有实现的接口,并持有InvocationHandler对象
     *
     * @return 代理类
     */
    T bind() {
        return (T) Proxy.newProxyInstance(originalObj.getClass().getClassLoader(),
                originalObj.getClass().getInterfaces(), this);
    }

    /**
     * @param proxy  代理对象
     * @param method 正在执行的方法
     * @param args   调用目标方法时传入的实参
     * @return 代理方法返回值
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long start = System.currentTimeMillis();
        System.out.println("start:" + start);
        //调用原始类的方法(具体那个方法会根据原始类动态调用的方法决定)
        Object invoke = method.invoke(originalObj, args);
        long end = System.currentTimeMillis();
        System.out.println("end:" + end);
        System.out.println(originalObj.getClass().getName() + "#" + method.getName() + " 方法耗时:" + (end - start));
        return invoke;
    }
}

基于CGLib实现

利用 ASM 框架,对代理对象类生成的 class 文件加载进来,通过修改其字节码生成子类来进行代理。 注意:如果想要使用 CGlib 动态代理,那么代理类不能使用 final 修饰类和方法。

HOW

<dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib</artifactId>
  <version>3.3.0</version>
</dependency>
public interface UserService {

    void addUser();

    void updateUser(String str);

}

/**
* 被代理类
*/
public class UserServiceImpl {

    public void addUser() {
        System.out.println("添加了一个用户");
    }

    public void deleteUser() {
        System.out.println("删除了一个用户");
    }
}

/**
* 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;
    }

}
public class test {
    public static void main(String[] args) {
        UserServiceCGlib serviceCGlib = new UserServiceCGlib(new UserServiceImpl());
        UserServiceImpl userService = (UserServiceImpl)serviceCGlib.getProxyInstance();
        userService.addUser();
        System.out.println();
        userService.deleteUser();
    }
}

=======console=======
增强开始~~~
添加了一个用户
增强结束~~~

增强开始~~~
删除了一个用户
增强结束~~~

场景

Spring AOP
以下是 Spring AOP 创建代理的方法:

@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {

   if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
      Class<?> targetClass = config.getTargetClass();
      if (targetClass == null) {
         throw new AopConfigException("TargetSource cannot determine target class: " +
               "Either an interface or a target is required for proxy creation.");
      }
      //如果
      if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
         return new JdkDynamicAopProxy(config);
      }
      return new ObjenesisCglibAopProxy(config);
   }
   else {
      return new JdkDynamicAopProxy(config);
   }
}
  1. 如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理
  2. 如果目标对象实现了接口,也可以强制使用 CGLIB3、如果目标对象没有实现了接口,必须采用 CGLIB 库,spring 会自动在 JDK 动态代理和 CGLIB 之间转换。

如果需要强制使用 CGLIB 来实现 AOP,需要配置 spring.aop.proxy-target-class=true 或@EnableAspectJAutoProxy(proxyTargetClass= true