文章目录
动态代理是什么?
动态代理是在程序运行的过程中动态生成代理类的字节码文件,然后把字节码文件加载到方法区,生成一个具体的class对象,然后反射拿到构造方法进行实例化,获取代理类
JDK动态代理实现
首先需要一个接口
public interface MyCalculator {
void sout();
}
然后写一个普通类实现接口
public class Calculator implements MyCalculator {
@Override
public void sout() {
System.out.println("普通计算器!");
}
}
接着写一个获取代理类的方法
public class CalculatorProxy {
public static MyCalculator getProcy( Calculator calculator) {
//获取类加载器
ClassLoader classLoader = calculator.getClass().getClassLoader();
//获取实现的接口
Class<?>[] interfaces = calculator.getClass().getInterfaces();
//包裹原来方法的增强
InvocationHandler h = new InvocationHandler() {
@Override
//invoke方法是对原来的方法包裹并且可以在前后进行增强
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
System.out.println("前面加强");
//执行原本的方法
result = method.invoke(calculator, args);
System.out.println("后面加强");
return result;
}
};
Object o = Proxy.newProxyInstance(classLoader, interfaces, h);
return (MyCalculator) o;
}
}
进行测试
public static void main(String[] args) {
MyCalculator procy = CalculatorProxy.getProcy(new Calculator());
procy.sout();
}
JDK动态代理源码解析——代理类怎么生成的?
//关键点 获取代理对象
Object o = Proxy.newProxyInstance(classLoader, interfaces, h);
点进newProxyInstance方法
//获取代理类对象
Class<?> cl = getProxyClass0(loader, intfs);
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
//代理的类接口最多实现65535个
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
//proxyClassCache.get(loader, interfaces);这行代码中,其实主要就是调用了WeakCache 的get方法
// WeakCache是一个二级缓存实现。其存储方式主要使用ConcurrentMap,从WeakCache缓存获取的时候,需要两个参数,class loader 和 interface 数组
return proxyClassCache.get(loader, interfaces);
}
get方法本质是从二级缓存里获取代理对象
//缓存的底层实现, key为一级缓存, value为二级缓存。 为了支持null, map的key类型设置为Object
private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map = new ConcurrentHashMap<>();
-
一级缓存 key:classLoader ( value:二级缓存 key:(classLoader 和 接口 通过规则生成二级缓存的key) value:Supplier)
二级缓存 key:(classLoader 和 接口 通过规则生成二级缓存的key) value:supplier有可能是一个Factory,或者是一个CacheValue[生成代理对象之后将其包装成CacheValue放入二级缓存的value中并且将没有包装的value直接返回。而因为将生成的结果放入二级缓存当中,如果下次获取或者其他线程来获取的时候就可以直接得到一个CacheValue实例,再通过get就能获取到代理对象。] -
一级缓存的key是弱引用,二级缓存是强引用。其中key是根据入参直接传入的,二级缓存的key和value都是是根据一级缓存的key和value通过各自的工厂方法(subKeyFactory和valueFactory)计算得到的。当弱引用被clear后,entries会被以惰性(lazily)方式被删除(在调用get方法后,第二行代码)。
作用:使用弱引用做一级key,二级缓存中cachevalue也是弱引用,解决内存泄露问题
get方法源码解析
public V get(K key, P parameter) {
//这里要求实现的接口不能为空
Objects.requireNonNull(parameter);
//清除过期的缓存(依靠弱引用机制),WeakCache的所有方法调用都会先进行这一步处理
expungeStaleEntries();
//将ClassLoader包装成CacheKey, 作为一级缓存的key
Object cacheKey = CacheKey.valueOf(key, refQueue);
//获取得到二级缓存
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
//如果根据ClassLoader没有获取到对应的值
if (valuesMap == null) {
//以CAS方式放入, 如果不存在则放入,否则返回原先的值
ConcurrentMap<Object, Supplier<V>> oldValuesMap = map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
//如果oldValuesMap有值, 说明放入失败
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
//根据代理类实现的接口数组来生成二级缓存key, 分为key0, key1, key2, keyx
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
//这里通过subKey获取到二级缓存的值
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
//这个循环提供了轮询机制, 如果条件为假就继续重试直到条件为真为止
while (true) {
//如果通过subKey取出来的值不为空
if (supplier != null) {
//在这里supplier可能是一个Factory也可能会是一个CacheValue
//在这里不作判断, 而是在Supplier实现类的get方法里面进行验证
V value = supplier.get();
if (value != null) {
return value;
}
}
if (factory == null) {
//新建一个Factory实例作为subKey对应的值
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
//到这里表明subKey没有对应的值, 就将factory作为subKey的值放入
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
//到这里表明成功将factory放入缓存
supplier = factory;
}
//否则, 可能期间有其他线程修改了值, 那么就不再继续给subKey赋值, 而是取出来直接用
} else {
//期间可能其他线程修改了值, 那么就将原先的值替换
if (valuesMap.replace(subKey, supplier, factory)) {
//成功将factory替换成新的值
supplier = factory;
} else {
//替换失败, 继续使用原先的值
supplier = valuesMap.get(subKey);
}
}
}
}
get方法里做了什么?
- 首先清除过期的缓存(依靠弱引用机制),防止内存泄露
- 将ClassLoader包装成CacheKey, 作为一级缓存的key
- map.get(cacheKey)获得二级缓存,如果为null则以CAS方式放入(不存在则放入),否则返回存在的值
- 根据class loader 和 interface 数组按照一定的规则生成二级缓存subKey
- valuesMap.get(subKey)获得二级缓存的value
- 从缓存中获取value,当缓存获取不到值得时候,首先会实例化一个Factory 放入二级缓存的value中,等再次获取或者其他线程同时获取的时候,就可以得到一个Factory实例
- 然后执行 V value = supplier.get(); 其中supplier就是二级缓存的value,它有可能是一个Factory,也可能是一个CacheValue
- 如果二级缓存的value是Factory实例,通过其的get方法可以生成代理对象返回(Factory这个内部工厂类,可以看到它的get方法是使用synchronized关键字进行了同步),生成代理对象之后将其包装成CacheValue放入二级缓存的value中并且将没有包装的value直接返回。因为将生成的结果放入二级缓存当中,所以下次获取或者其他线程来获取的时候就可以直接得到一个CacheValue实例,再通过get就能获取到代理对象
//获取代理类对象构造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
//通过代理类的构造器反射出代理对象
return cons.newInstance(new Object[]{h});
JDK代理类方法如何调用的被代理类方法?
代理类源码:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.sun.proxy;
import JDKproxy.MyCalculator;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements MyCalculator {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
//代理方法在这
public final void sout() throws {
try {
/**
*super 就是代表proxy类,类持有一个protected InvocationHandler h;属性
*这个属性是我们在构造代理类的时候传入的:
*Object o = Proxy.newProxyInstance(classLoader, interfaces, h);
*h的invoke方法里就包含了我们的增强逻辑以及原始方法的调用
*/
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
//我们的测试方法
m3 = Class.forName("JDKproxy.MyCalculator").getMethod("sout");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
- 代理类通过static块 m3 = Class.forName(“JDKproxy.MyCalculator”).getMethod(“sout”);拿到原本方法对象
- 然后在代理方法 sout()中 super.h.invoke(this, m3, (Object[])null);
- super 就是代表proxy类,类持有一个protected InvocationHandler h;属性
- 这个属性是我们在构造代理类的时候传入的:
- Object o = Proxy.newProxyInstance(classLoader, interfaces, h);
- h的invoke方法里就包含了我们的增强逻辑以及原始方法的调用
JDK动态代理为什么必须要基于接口?
通过上面的分析,我们已经知道了代理对象是如何生成的了,那么回到开头的问题,为什么jdk的动态代理一定要基于接口呢?
其实如果不看上面的分析,我们也应该知道,要扩展一个类有常见的两种方式,继承父类或实现接口。这两种方式都允许我们对方法的逻辑进行增强,但现在不是由我们自己来重写方法,而是要想办法让jvm去调用InvocationHandler中的invoke方法,也就是说代理类需要和两个东西关联在一起:
-
被代理类
-
invocationHandler
而jdk处理这个问题的方式是选择继承父类Proxy,并把InvocationHandler存在父类的对象中:
public class Proxy implements java.io.Serializable {
protected InvocationHandler h;
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
//...
}
通过父类Proxy的构造方法,保存了创建代理对象过程中传进来的InvocationHandler的实例,使用protected修饰保证了它可以在子类中被访问和使用。但是同时,因为java是单继承的,因此在继承了Proxy后,只能通过实现目标接口的方式来实现方法的扩展,达到我们增强目标方法逻辑的目的。
CGLIB动态代理实现
如果不是spring项目首先要引入依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.12</version>
</dependency>
写一个普通类
public class CGCalculator {
public void run() {
System.out.println("普通计算器!");
}
}
写一个方法拦截器
public class MyCglib implements MethodInterceptor {
/**
*
* @param obj 表示增强的对象,即实现这个接口类的一个对象
* @param method 表示要拦截的方法
* @param objects 表示被拦截方法的参数
* @param methodProxy 表示要触发父类的方法对象
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("方法加强前");
Object o = methodProxy.invokeSuper(obj, objects);
System.out.println("方法加强后");
return o;
}
}
进行测试
public class Test6 {
public static void main(String[] args) {
//创建cglib获取代理对象的操作对象
Enhancer enhancer = new Enhancer();
//设置enhancer对象的父类
enhancer.setSuperclass(CGCalculator.class);
//设置enhancer的回调对象
enhancer.setCallback(new MyCglib());
//创建代理对象
CGCalculator cgCalculator = (CGCalculator)enhancer.create();
cgCalculator.run();
}
}
CGLIB动态代理源码解析——代理类怎么生成的?
首先代理类对象是调用enhancer.create()生成的,其中会调用super.create()。
做的事情是:通过cglib的生成策略生成代理类的字节码文件,然后加载到jvm,再通过反射创建代理类对象。
public abstract class AbstractClassGenerator<T> implements ClassGenerator {
protected Object create(Object key) {
try {
ClassLoader loader = this.getClassLoader();
Map<ClassLoader, AbstractClassGenerator.ClassLoaderData> cache = CACHE;
AbstractClassGenerator.ClassLoaderData data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);
if (data == null) {
Class var5 = AbstractClassGenerator.class;
synchronized(AbstractClassGenerator.class) {
cache = CACHE;
data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);
if (data == null) {
Map<ClassLoader, AbstractClassGenerator.ClassLoaderData> newCache = new WeakHashMap(cache);
data = new AbstractClassGenerator.ClassLoaderData(loader);
newCache.put(loader, data);
CACHE = newCache;
}
}
}
this.key = key;
//1.获取代理类对象
Object obj = data.get(this, this.getUseCache());
return obj instanceof Class ? this.firstInstance((Class)obj) : this.nextInstance(obj);
}
...
}
public Object get(AbstractClassGenerator gen, boolean useCache) {
if (!useCache) {
//2.生成代理类对象
return gen.generate(this);
} else {
Object cachedValue = this.generatedClasses.get(gen);
return gen.unwrapCachedValue(cachedValue);
}
}
protected Class generate(AbstractClassGenerator.ClassLoaderData data) {
Object save = CURRENT.get();
CURRENT.set(this);
try {
ClassLoader classLoader = data.getClassLoader();
if (classLoader == null) {
throw new IllegalStateException("ClassLoader is null while trying to define class " + this.getClassName() + ". It seems that the loader has been expired from a weak reference somehow. Please file an issue at cglib's issue tracker.");
} else {
String className;
synchronized(classLoader) {
className = this.generateClassName(data.getUniqueNamePredicate());
data.reserveName(className);
this.setClassName(className);
}
Class gen;
if (this.attemptLoad) {
try {
gen = classLoader.loadClass(this.getClassName());
Class var25 = gen;
return var25;
} catch (ClassNotFoundException var20) {
}
}
//3.生成代理类字节码
byte[] b = this.strategy.generate(this);
className = ClassNameReader.getClassName(new ClassReader(b));
ProtectionDomain protectionDomain = this.getProtectionDomain();
synchronized(classLoader) {
if (protectionDomain == null) {
//4.根据字节码生成代理类对象
gen = ReflectUtils.defineClass(className, b, classLoader);
} else {
gen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain);
}
}
Class var8 = gen;
return var8;
}
}...
}
}
CGLIB代理类方法如何调用的被代理类方法?
生成完代理类会生成如下三个文件,其中第2个是代理类class,其余两个是代理类的fastclass和被代理类fastclass
我们先粗略看一下代理类class中的内容,代理类run()方法中,会调用方法拦截器的intercept()方法,方法拦截器会通过某些手段找到代理类的CGLIB$run$0()方法,最后实现调用父类即被代理类Person的run()方法,这个手段在Jdk中是反射,而在Cglib中是一种新的机制-FastClass机制。
public class Person$$EnhancerByCGLIB$$c7ae5d4e extends Person implements Factory {
private static final Method CGLIB$run$0$Method;
private static final MethodProxy CGLIB$run$0$Proxy;
static void CGLIB$STATICHOOK1() {
Class var0 = Class.forName("com.codezhao.designpattern.proxypattern.cglibproxy.Person$$EnhancerByCGLIB$$c7ae5d4e");
Class var1;
CGLIB$run$0$Method = ReflectUtils.findMethods(new String[]{"run", "()V"}, (var1 = Class.forName("com.codezhao.designpattern.proxypattern.cglibproxy.Person")).getDeclaredMethods())[0];
CGLIB$run$0$Proxy = MethodProxy.create(var1, var0, "()V", "run", "CGLIB$run$0");
}
final void CGLIB$run$0() {
super.run();
}
public final void run() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$run$0$Method, CGLIB$emptyArgs, CGLIB$run$0$Proxy);
} else {
super.run();
}
}
}
FastClass机制
由于反射的效率较低,Cglib采用FastClass机制实现被代理类方法的调用,FastClass机制就是给一个类的方法建立索引,通过索引直接调用方法。
调用某Test对象的run()方法,FastClass机制实现:
/**
* @author codeZhao
* @date 2021/2/20 19:46
* @Description
*/
public class FastClassTest {
public static void main(String[] args) {
Test test = new Test();
Test$FastClass test$FastClass = new Test$FastClass();
//先获取方法索引
int index = test$FastClass.getIndex("run()v");
//通过invoke()传入方法索引和类对象,执行索引对应方法
test$FastClass.invoke(index, test);
}
}
class Test {
public void f() {
System.out.println("f()");
}
public void run() {
System.out.println("run()");
}
}
class Test$FastClass {
public Object invoke(int index, Object o) {
Test t = (Test) o;
switch (index) {
case 1:
t.f();
return null;
case 2:
t.run();
return null;
}
return null;
}
public int getIndex(String signature) {
switch (signature.hashCode()) {
case 3078511://"f()v的hashCode"
return 1;
case 3108302://"run()v的hashCode"
return 2;
}
return -1;
}
}
动态调用时,假设要调用某Test对象的run()方法,FastClass机制实现方式是首先通过getIndex()传入方法签名信息,获取方法的索引,然后通过invoke()传入方法索引和类对象,执行索引对应方法
反射来实现:
Test test = new Test();
//通过反射拿到方法
Method method = Class.forName("Test").getMethod("run");
//invoke直接传入方法
method.invoke(test);
FastClass机制和反射相比,构建时比较复杂,但运行效率高于反射
代理对象生成后,会产生一个同名的fun方法用于前端调用,该方法内部会将请求传递到拦截对象中。
除此之外,代理对象中还会生产一个CGLIB$fun$1方法,该方法会调用父类(被代理对象)的fun方法。
在使用invokeSuper的过程中,会经拦截器、FastClass走到CGLIB$fun$1这里,
然后在CGLIB$fun$1中请求父类的fun方法。
大致过程如下图所示:
invokeSuper调用前后的主要流程如下图所示
- 假设前端触发了方法名为fun调用,首先进入代理对象中的同名方法
- 然后进入自定义的方法拦截对象MethodInterceptor,图中的this也就是代理对像
- 在调用invokeSuper后,cglib内部会通过代理类的FastClass找到要执行的方法,此时传到FastClass中的对象是代理对象,所以在通过FastClass对象后,流程会重新走到代理对象中
- ==但此时不会再次调用fun同名方法,而是调用了一个CGLIB$fun$1的方法,该方法会调用父类(也就是被代理类)fun方法,==至此流程结束。
如果将MethodInterceptor中的invokeSuper改为invoke,会怎么样呢?会出现StackOverflowError,同时invoke之前的内容会被反复执行。请看如下流程图