Cglib是一个高效的动态代理方式,并且在SpringBoot中默认的代理实现方式。
本片文章会分析Cglib是如何生成代理对象、代理对象如何执行相应的目标函数、及和jdk代理有什么区别。validatedClasses 是静态容器,是对在系统中所有通过cglib创建的目标对象中是否有final method的检查。因为Cglib代理在继承目标对象时要复写所有函数,不可以复写final修饰的函数。
在构建代理对象时通过通知规则来配置。这个通知规则包含了通知器和通知方法。
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
try {
// 取到通知规则中要被代理的目标对象的 Class对象,由于要继承所以不能为空
Class<?> rootClass = this.advised.getTargetClass();
Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
Class<?> proxySuperClass = rootClass;
// 如果目标对象是一个Cglib的对象,会去拿它的父类对象,这个对象一定是真正的目标对象
if (ClassUtils.isCglibProxyClass(rootClass)) {
proxySuperClass = rootClass.getSuperclass();
// 另外还拿到目标对象一堆接口,收集起来后面有用
Class<?>[] additionalInterfaces = rootClass.getInterfaces();
for (Class<?> additionalInterface : additionalInterfaces) {
this.advised.addInterface(additionalInterface);
}
}
// 这里会去检查目标对象的final method,以及当前classLoader可不可以加载到这个目标对象,先去看这个方法
validateClassIfNecessary(proxySuperClass, classLoader);
// 这一步是直接new个增强器出来
Enhancer enhancer = createEnhancer();
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
if (classLoader instanceof SmartClassLoader &&
((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
enhancer.setUseCache(false);
}
}
// 给增强器配置一些参数,目标对象、目标接口、命名策略(BySpringCGLIB)
enhancer.setSuperclass(proxySuperClass);
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
// 拿到所有的回调对象,主要就是aop的通知拦截器,目标对象方法拦截器(MethodInteceptor)
Callback[] callbacks = getCallbacks(rootClass);
Class<?>[] types = new Class<?>[callbacks.length];
for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
// fixedInterceptorMap only populated at this point, after getCallbacks call above
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);
// 用增强器和一堆拦截器创建代理Class对象和实例
return createProxyClassAndInstance(enhancer, callbacks);
}
catch (CodeGenerationException | IllegalArgumentException ex) {
throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
": Common causes of this problem include using a final class or a non-visible class",
ex);
}
catch (Throwable ex) {
// TargetSource.getTarget() failed
throw new AopConfigException("Unexpected AOP exception", ex);
}
}
插播一下上面提到给增强器设置了一堆回调数组,这里可以看到实际上是一些拦截器,DynamicAdvisedInterceptor 是动态通知拦截器,应该就是AOP的通知
private void validateClassIfNecessary(Class<?> proxySuperClass, @Nullable ClassLoader proxyClassLoader) {
if (logger.isWarnEnabled()) {
// 锁住容器
synchronized (validatedClasses) {
// 如果这个对象没检查过
if (!validatedClasses.containsKey(proxySuperClass)) {
doValidateClass(proxySuperClass, proxyClassLoader,
ClassUtils.getAllInterfacesForClassAsSet(proxySuperClass));
// 检查完后,如果合法就放进静态容器中缓存,整个Spring生命周期存在
validatedClasses.put(proxySuperClass, Boolean.TRUE);
}
}
}
}
看看是怎么检查的,只是打个日志,没做强制措施
private void doValidateClass(Class<?> proxySuperClass, @Nullable ClassLoader proxyClassLoader, Set<Class<?>> ifcs) {
// 顶级Object类就不检查了
if (proxySuperClass != Object.class) {
// 直接拿到目标对象所有声明的方法遍历
Method[] methods = proxySuperClass.getDeclaredMethods();
for (Method method : methods) {
int mod = method.getModifiers();
// 如果某个方法的不是静态的也不是private的,侧面说明Cglib不复写静态方法和private方法
if (!Modifier.isStatic(mod) && !Modifier.isPrivate(mod)) {
if (Modifier.isFinal(mod)) {
// final method 代理对象不会复写final method,因此只是打个日志记录
if (implementsInterface(method, ifcs)) {
logger.info("Unable to proxy interface-implementing method [" + method + "] because " +
"it is marked as final: Consider using interface-based JDK proxies instead!");
}
// final method 代理对象不会复写final method,因此只是打个日志记录
logger.debug("Final method [" + method + "] cannot get proxied via CGLIB: " +
"Calls to this method will NOT be routed to the target instance and " +
"might lead to NPEs against uninitialized fields in the proxy instance.");
}
// 如果方法不是公开的又不是包内可见的且对当前 classLoader不可见,打个日志
else if (!Modifier.isPublic(mod) && !Modifier.isProtected(mod) &&
proxyClassLoader != null && proxySuperClass.getClassLoader() != proxyClassLoader) {
logger.debug("Method [" + method + "] is package-visible across different ClassLoaders " +
"and cannot get proxied via CGLIB: Declare this method as public or protected " +
"if you need to support invocations through the proxy.");
}
}
}
// 递归地向上检查一直到顶层Object
doValidateClass(proxySuperClass.getSuperclass(), proxyClassLoader, ifcs);
}
}
因为Spring默认用 ObjenesisCglibAopProxy对象,所以看它的实现方法
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
// 这里是最关键的地方,就是从 Enhancer.createClass() 开始生成代理对象的Class对象,
// 这里有点区别的是一般我们使用 Enhancer.create() 直接创建代理对象,这里只是生成了Class
Class<?> proxyClass = enhancer.createClass();
if (objenesis.isWorthTrying()) {
proxyInstance = objenesis.newInstance(proxyClass, enhancer.getUseCache());
logger.debug("Unable to instantiate proxy using Objenesis, " +
"falling back to regular proxy construction", ex);
// Regular instantiation via default constructor...
Constructor<?> ctor = (this.constructorArgs != null ?
proxyClass.getDeclaredConstructor(this.constructorArgTypes) :
proxyClass.getDeclaredConstructor());
ReflectionUtils.makeAccessible(ctor);
proxyInstance = (this.constructorArgs != null ?
ctor.newInstance(this.constructorArgs) : ctor.newInstance());
throw new AopConfigException("Unable to instantiate proxy using Objenesis, " +
"and regular proxy instantiation via default constructor fails as well", ex);
// 这里也是很关键的地方,将代理对象和一堆拦截器关联起来,
// 在拦截器的 MethodInteceptor.intercpt() 函数通过 methodProxy.invokeSuper() 调用目标对象对应方法
((Factory) proxyInstance).setCallbacks(callbacks);
最后九曲十八弯,追踪到 AbstractClassGenerator generate() 函数
protected Class generate(ClassLoaderData data) {
byte[] b = strategy.generate(this);
来到 DefaultGeneratorStrategy,是导入 ASM 一个高性能的Class字节码编辑框架处理的,这里实际也没做什么,只是创建了DebuggingClassWriter对象,generateClass(cw) 是调用Enhancer实现的方法。Enhancer.generateClass(cw) 里面是拿到非私有化的构造函数和目标对象、目标接口的方法,通过asm的api编辑定义代理对象字节码。
由 CglibAopProxy.getProxy() 方法发起整个创建Aop代理对象的流程,其中会涉及到代理对象的Class字节码数组生成,根据字节码数组反射定义Class对象,将Class对象缓存,根据Class对象反射实例化代理对象实例。
ObjenesisCglibAopProxy.createProxyClassAndInstance() 函数调用 Enhancer对象的一系列方法,最后是由Enhancer.generateClass(cw)拿到目标对象、目标接口的方法,用ams框架定义生成代理对象字节码数组返回。
调用链最后回到 ObjenesisCglibAopProxy.createProxyClassAndInstance(enhancer, callbacks) 中,拿到Class对象的构造函数用反射实例化代理对象,并将实参 callbacks 设置进代理对象中关联起来。
Cglib代理对象的工作原理
现在大致了解Cglib是如何生成代理对象的了,但是对于代理对象如何与目标对象关联起来的,调用代理对象的sayHello()方法时是如何最终调用到
首先要介绍 MethodInteceptor 接口,跟Jdk Dynamic Proxy的InvocationHandler一样,本质思想就是一个回调接口,
用户自定义对象实现该接口的 intercept(),在里面编写一些处理逻辑,例如开启事务,记录调用日志等...
这个拦截器在Cglib创建代理对象时会被注入,Enhancer.setCallback(MethodInteceptor)。在Spring AOP源码中是等代理对象实例化后注入。很像JDK代理吧,也是代理对象内部持有InvocationHandler对象。
进一步看代理对象内部的实现,可以通过把代理对象的Class字节码写入磁盘中,Cglib提供了API做到这点。
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code\\cglib");
现在就来看下这个被增强过的代理对象是如何调用到目标对象的,CustomServiceImpl中有 sayHello() doGoodJob()函数
代理对象继承了目标对象,实现了Factory接口,复写了全部目标对象的非final方法。
其中我都用加重标出来了。还有 Callback[] 拦截器数组和当初创建代理对象时注入的方法拦截器,
这里只有一个MethodInterceptor 因为我在创建代理时只给它设置了一个。
Enhancer有一个setCallbacks(Callback[] callbacks)函数。
向下看,sayHello()中先去尝试拿拦截器,然后不为空就去执行拦截器intercept()逻辑,
将当前代理对象和sayHello()方法,方法参数,代理方法都传进去,用户在interceptor()中自由处理。
这就是Cglib代理的真正工作原理,通过设置拦截器,在拦截器中编写逻辑,来达到某些目的。而拦截器早在Enhancer生产代理对象