写在前面
前面介绍了Spring AOP的JDK动态代理的过程,这一篇文章就要介绍下Spring AOP的Cglib代理过程。
CGLib全称为Code Generation Library,是一个强大的高性能,高质量的代码生成类库,可以在运行期扩展Java类与实现Java接口,CGLib封装了asm,可以再运行期动态生成新的class。和JDK动态代理相比较:JDK创建代理有一个限制,就是只能为接口创建代理实例,而对于没有通过接口定义业务方法的类,则可以通过CGLib创建动态代理
使用cglib 创建动态代理,需要注意几点:
1. cglib是针对类实现动态代理的,他的原理是对指定类生产一个子类,并覆盖其中的方法来实现增强。但是因为采用的是继承,所以不能对使用final修饰的类进行代理。
2. cglib底层是通过使用一个小而块的字节码处理框架asm,来转换字节码生产并生产新的类;(不推荐直接使用asm,因为他要求你必须对jvm内部结构包括class文件的格式和指令集都很熟悉)
aop接口
JDK动态代理是由JdkDynamicAopProxy来生成代理对象的,Cglib则是由CglibAopProxy来生成代理对象的。JdkDynamicAopProxy、CglibAopProxy实现了AopProxy接口,如下:
public interface AopProxy { Object getProxy(); Object getProxy(ClassLoader classLoader); }
然后详细看下CglibProxy的代理对象的生成过程。CglibProxy、JdkDynamicAopProxy都拥有一个非常重要的属性AdvisedSupport advised这个属性包含了拦截的配置信息,这个属性在JdkDynamicAopProxy中已经说过了,不再详细说明。 CglibAopProxy中 的getProxy方法中主要通过产生cglib动态代理,并且构造代理的回调方法(MethodInterceptor的实现类),并在其子类(ObjenesisCglibAopProxy)中有自己对于动态代理的获取,已经与回调方法的绑定。
@Override public Object getProxy(ClassLoader classLoader) {try { Class<?> rootClass = this.advised.getTargetClass(); Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy"); Class<?> proxySuperClass = rootClass;
//这里判断rootClass是否是Cglib代理所产生的类(内部判断rootClass的className是否包含$$),对于本工程肯定不符合,跳过 if (ClassUtils.isCglibProxyClass(rootClass)) { proxySuperClass = rootClass.getSuperclass(); Class<?>[] additionalInterfaces = rootClass.getInterfaces(); for (Class<?> additionalInterface : additionalInterfaces) { this.advised.addInterface(additionalInterface); } } // //验证proxySuperClass中的是否有final方法(仅仅是打印出来警告信息,不做任何处理) validateClassIfNecessary(proxySuperClass, classLoader); // Configure CGLIB Enhancer...使用Enhancer 来构造cglib代理对象(就是使用Enhancer设置下要继承的父类、设置下要实现的接口、设置下回调然后就创建出代理对象。) Enhancer enhancer = createEnhancer(); if (classLoader != null) { enhancer.setClassLoader(classLoader); if (classLoader instanceof SmartClassLoader && ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) { enhancer.setUseCache(false); } } enhancer.setSuperclass(proxySuperClass); enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised)); enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE); enhancer.setStrategy(new UndeclaredThrowableStrategy(UndeclaredThrowableException.class));
//获取cglib代理对象的回调 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); // (生成代理对象的字节码,并构造实例,创建回调) return createProxyClassAndInstance(enhancer, callbacks); } catch (CodeGenerationException ex) { throw new AopConfigException("Could not generate CGLIB subclass of class [" + this.advised.getTargetClass() + "]: " + "Common causes of this problem include using a final class or a non-visible class", ex); } catch (IllegalArgumentException ex) { throw new AopConfigException("Could not generate CGLIB subclass of class [" + this.advised.getTargetClass() + "]: " + "Common causes of this problem include using a final class or a non-visible class", ex); } catch (Exception ex) { // TargetSource.getTarget() failed throw new AopConfigException("Unexpected AOP exception", ex); } }
上述内容,就是使用Enhancer设置下要继承的父类、设置下要实现的接口、设置下回调然后就创建出代理对象。其中的一个重要的回调Callback为DynamicAdvisedInterceptor,在DynamicAdvisedInterceptor的intercept方法里面实现了和JDK动态代理同样类似的逻辑。
重要的回调Callback为DynamicAdvisedInterceptor
private Callback[] getCallbacks(Class<?> rootClass) throws Exception { ...//(回调其实是一个拦截器interceptor) Choose an "aop" interceptor (used for AOP calls). Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised); Callback[] mainCallbacks = new Callback[]{ aopInterceptor, // for normal advice targetInterceptor, // invoke target without considering advice, if optimized new SerializableNoOp(), // no override for methods mapped to this targetDispatcher, this.advisedDispatcher, new EqualsInterceptor(this.advised), new HashCodeInterceptor(this.advised) };return mainCallbacks; }
在DynamicAdvisedInterceptor的intercept方法里面实现了和JDK动态代理同样类似的逻辑
@Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object oldProxy = null; boolean setProxyContext = false; Class<?> targetClass = null; Object target = null; try { if (this.advised.exposeProxy) { // Make invocation available if necessary. oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } // May be null. Get as late as possible to minimize the time we // "own" the target, in case it comes from a pool... target = getTarget(); if (target != null) { targetClass = target.getClass(); } List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); Object retVal; // Check whether we only have one InvokerInterceptor: that is, // no real advice, but just reflective invocation of the target. if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) { // We can skip creating a MethodInvocation: just invoke the target directly. // Note that the final invoker must be an InvokerInterceptor, so we know // it does nothing but a reflective operation on the target, and no hot // swapping or fancy proxying. retVal = methodProxy.invoke(target, args); } else { // We need to create a method invocation... retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed(); } retVal = processReturnType(proxy, target, method, retVal); return retVal; } finally { if (target != null) { releaseTarget(target); } if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); } } }
上面和JDK动态代理一样也是分两大步,第一步获取一个拦截器链,第二步创建一个MethodInvocation来执行这个拦截器链。
第一步:和JDK动态代理获取拦截器链的过程一样的。
第二步:它创建的MethodInvocation是CglibMethodInvocation,它是继承了JDK动态代理所创建的ReflectiveMethodInvocation,覆写了ReflectiveMethodInvocation的invokeJoinpoint方法。ReflectiveMethodInvocation的invokeJoinpoint方法内容如下:
private static class CglibMethodInvocation extends ReflectiveMethodInvocation {
private final MethodProxy methodProxy;
private final boolean publicMethod;
public CglibMethodInvocation(Object proxy, Object target, Method method, Object[] arguments,
Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {
super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
this.methodProxy = methodProxy;
//his.publicMethod就是说明所调用的方法是否是public类型的
this.publicMethod = Modifier.isPublic(method.getModifiers());
}
//CglibMethodInvocation的invokeJoinpoint方法
protected Object invokeJoinpoint() throws Throwable {
if (this.publicMethod) {
//目标方法是public方法
return this.methodProxy.invoke(this.target, this.arguments);
}
else {
//(目标方法非public方法)AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
return super.invokeJoinpoint();
}
}
}
重要
在构建CglibMethodInvocation这个MethodInvocation时进行赋值的。Modifier.isPublic(method.getModifiers());就是判断该方法是否是public类型的。
CglibMethodInvocation与ReflectiveMethodInvocation仅仅在执行目标方法的时候有所不同,当目标方法是public方法时,ReflectiveMethodInvocation一直采用反射的策略执行目标方法。而CglibMethodInvocation却使用this.methodProxy.invoke(this.target, this.arguments)代理方法来执行
当执行public方法时,会比反射有一个更好的性能。然而当我们在使用cglib的callback的时候却还是使用反射,没有去使用MethodProxy。因此我们还是按照源码的使用方式来使用,来提升性能。
本文章中许多步骤省略了,是因为在上一篇SpringAOP JDK的动态代理文章中都进行了详细介绍。
JDK动态代理和CGLib的比较
CGLib所创建的动态代理对象的性能比JDK所创建的代理对象性能高不少,大概10倍,但CGLib在创建代理对象时所花费的时间却比JDK动态代理多大概8倍,所以对于singleton的代理对象或者具有实例池的代理,因为无需频繁的创建新的实例,所以比较适合CGLib动态代理技术,反之则适用于JDK动态代理技术。
另外,由于CGLib采用动态创建子类的方式生成代理对象,所以不能对目标类中的final,private等方法进行处理。所以,大家需要根据实际的情况选择使用什么样的代理了。