一.概念
动态代理是一种方便运行时动态构建代理、动态处理代理方法调用的机制
动态代理的方式:
- 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 字节码插桩技术