动态代理

简介: 动态代理

一.概念

动态代理是一种方便运行时动态构建代理、动态处理代理方法调用的机制

动态代理的方式:

  • JDK 自身提供的动态代理,利用反射机制
  • cglib 动态代理,基于 ASM

## 二.场景

  • 面向切面的编程(AOP)
  • RPC 调用

三.代理解决的问题

首先,它是一个代理机制。如果熟悉设计模式中的代理模式,我们会知道,代理可以看作是对调用目标的一个包装,这样我们对目标代码的调用不是直接发生的,而是通过代理完成。通过代理可以让调用者与实现者之间解耦。比如进行 RPC 调用,通过代理,可以提供更加友善的界面。还可以通过代理,可以做一个全局的拦截器。

四.使用

1.jdk 的动态代理

I.使用

publicinterfaceAnimal {

   voideat();

}

publicclassCatimplementsAnimal {

   @Override

   publicvoideat() {

       System.out.println("The cat is eating");

   }

}

public class Dog implements Animal {

   @Override

   public void eat() {

       System.out.println("The dog is eating");

   }

}

public class AnimalProxy implements InvocationHandler {

   private Object target; // 代理对象


   public Object getInstance(Object target) {

       this.target = target;

       // 取得代理对象

       return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);

   }


   @Override

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

       System.out.println("调用前");

       Object result = method.invoke(target, args); // 方法调用

       System.out.println("调用后");

       return result;

   }

}

public class Test {

   public static void main(String[] args) {

       // JDK 动态代理调用

       AnimalProxy proxy = new AnimalProxy();

       Animal dogProxy = (Animal) proxy.getInstance(new Dog());

       dogProxy.eat();

   }

}

  • JDK Proxy 是通过实现 InvocationHandler 接口来实现的
  • 通过 newProxyInstance 获取到代理类对象
  • 重写 invoke 方法,以便代理类调用
  • JDK Proxy 只能代理实现接口的类

II.为什么 JDK Proxy 只能代理实现接口的类?

查看 newProxyInstance 的源码

/**

* @param   loader the class loader to define the proxy class

* @param   interfaces the list of interfaces for the proxy class to implement

*/

@CallerSensitive

public static Object newProxyInstance(ClassLoader loader,

                                     Class<?>[] interfaces,

                                     InvocationHandler h)

 throws IllegalArgumentException

{

 Objects.requireNonNull(h);

       final Class<?>[] intfs = interfaces.clone();

       final SecurityManager sm = System.getSecurityManager();

       if (sm != null) {

           checkProxyAccess(Reflection.getCallerClass(), loader, intfs);

       }

       /*

        * Look up or generate the designated proxy class.

        */

       Class<?> cl = getProxyClass0(loader, intfs);

    //省略其他代码

}

private static Class<?> getProxyClass0(ClassLoader loader,

                                      Class<?>... interfaces) {

 if (interfaces.length > 65535) {

   throw new IllegalArgumentException("interface limit exceeded");

 }


 // If the proxy class defined by the given loader implementing

 // the given interfaces exists, this will simply return the cached copy; 缓存

 // otherwise, it will create the proxy class via the ProxyClassFactory  代理类工厂

 return proxyClassCache.get(loader, interfaces);

}

public V get(K key, P parameter) {

 Objects.requireNonNull(parameter);//校验接口列表是否为null

 expungeStaleEntries();

 Object cacheKey = CacheKey.valueOf(key, refQueue);

 // lazily install the 2nd level valuesMap for the particular cacheKey

 ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);

 if (valuesMap == null) {

   ConcurrentMap<Object, Supplier<V>> oldValuesMap

     = map.putIfAbsent(cacheKey,

                       valuesMap = new ConcurrentHashMap<>());

   if (oldValuesMap != null) {

     valuesMap = oldValuesMap;

   }

 }

 // create subKey and retrieve the possible Supplier<V> stored by that

 // subKey from valuesMap

 Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));

 Supplier<V> supplier = valuesMap.get(subKey);

 Factory factory = null;

 while (true) {

   if (supplier != null) {

     // supplier might be a Factory or a CacheValue<V> instance

     V value = supplier.get();

     if (value != null) {

       return value;

     }

   }

   // else no supplier in cache

   // or a supplier that returned null (could be a cleared CacheValue

   // or a Factory that wasn't successful in installing the CacheValue)

   // lazily construct a Factory

   if (factory == null) {//没有缓存的时候,工厂也不存在,需要通过接口列表信息创建Factory

     factory = new Factory(key, parameter, subKey, valuesMap);

   }

   if (supplier == null) {

     supplier = valuesMap.putIfAbsent(subKey, factory);

     if (supplier == null) {

       // successfully installed Factory

       supplier = factory;

     }

     // else retry with winning supplier

   } else {

     if (valuesMap.replace(subKey, supplier, factory)) {

       // successfully replaced

       // cleared CacheEntry / unsuccessful Factory

       // with our Factory

       supplier = factory;

     } else {

       // retry with current supplier

       supplier = valuesMap.get(subKey);

     }

   }

 }

}

  • loader:为类加载器,也就是 target.getClass().getClassLoader()
  • interfaces:接口代理类的接口实现列表

根据注释说明,这个问题的源头,在于 JDK Proxy 的源码设计。首先会判断接口列表集合是否为空,为空直接抛出异常,不为空,如果缓存中不存在且工厂类也不存在,会通过接口列表信息创建工厂类.

如果要执意动态代理,非接口实现类就会报错.

2.cglib 动态代理

JDK 动态代理机制只能代理实现了接口的类,Cglib 是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对 final 修饰的类进行代理。

I.引入包

<dependency>

   <groupId>cglib</groupId>

   <artifactId>cglib</artifactId>

   <version>3.2.9</version>

</dependency>

II.代码实现

public class Panda {

   public void eat() {

       System.out.println("The panda is eating");

   }

}

public class CglibProxy implements MethodInterceptor {

   private Object target; // 代理对象


   public Object getInstance(Object target) {

       this.target = target;

       Enhancer enhancer = new Enhancer();

       // 设置父类为实例类

       enhancer.setSuperclass(this.target.getClass());

       // 回调方法

       enhancer.setCallback(this);

       // 创建代理对象

       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) {

       // CGLIB 动态代理调用

       CglibProxy proxy = new CglibProxy();

       Panda panda = (Panda) proxy.getInstance(new Panda());

       panda.eat();

   }

}

  • cglib 的调用通过实现 MethodInterceptor 接口的 intercept 方法;
  • 调用 invokeSuper 进行动态代理的;
  • 可以直接对普通类进行动态代理;

五.对比

JDK Proxy 的优势

  • 最小化依赖关系,减少依赖意味着简化开发和维护,JDK 本身的支持,更加可靠;
  • 平滑进行 JDK 版本升级,而字节码类库通常需要进行更新以保证在新版上能够使用;

Cglib 框架的优势

  • 可调用普通类,不需要实现接口;
  • 高性能;
  • 内部复杂,需要借助 Cglib 包,底层是 asm 字节码插桩技术
相关文章
|
Java 程序员
动态代理
动态代理
51 0
|
6月前
|
设计模式 Java
动态代理详解
【2月更文挑战第7天】
动态代理详解
|
6月前
|
设计模式 缓存 监控
静态代理与动态代理
静态代理与动态代理
41 0
jdk动态代理和cglb动态代理
jdk动态代理和cglb动态代理
|
Java Spring
jdk动态代理和cglib动态代理
只有聪明人才能看见的简介~( ̄▽ ̄~)~
87 0
jdk动态代理和cglib动态代理
|
存储 Java Apache
一文理解动态代理和静态代理
一文理解动态代理和静态代理
200 0
一文理解动态代理和静态代理
|
Java 编译器 Maven
动态代理竟然如此简单!(二)
这篇文章我们来聊一下 Java 中的动态代理。 动态代理在 Java 中有着广泛的应用,比如 AOP 的实现原理、RPC远程调用、Java 注解对象获取、日志框架、全局性异常处理、事务处理等。
97 0
动态代理竟然如此简单!(二)
|
设计模式 Java 程序员
动态代理竟然如此简单!(一)
这篇文章我们来聊一下 Java 中的动态代理。 动态代理在 Java 中有着广泛的应用,比如 AOP 的实现原理、RPC远程调用、Java 注解对象获取、日志框架、全局性异常处理、事务处理等。
151 0
动态代理竟然如此简单!(一)
深入理解动态代理
代理模式是在不修改原有代码逻辑的情况下,对原有代码逻辑增强的一种方式,要了解什么是动态代理,首先要知道什么是静态代理。
深入理解动态代理
|
Java 数据库连接 API
动态代理的实际应用
最近在用 Python 的 SQLAlchemy 库时(一个类似于 Hibernate 的 ORM 框架),发现它的 Events 事件还挺好用。 简单说就是当某张表的数据发生变化(曾、删、改)时会有一个事件回调,这样一些埋点之类的需求都可以实现在这里,同时和业务代码完全解耦,维护起来也很方便。