Spring AOP源码分析(五)Spring AOP的Cglib代理-阿里云开发者社区

开发者社区> 开发与运维> 正文

Spring AOP源码分析(五)Spring AOP的Cglib代理

简介:
上一篇文章介绍了Spring AOP的JDK动态代理的过程,这一篇文章就要介绍下Spring AOP的Cglib代理过程,仍然是使用上一篇文章的工程案例。 

JDK动态代理是由JdkDynamicAopProxy来生成代理对象的,Cglib则是由CglibAopProxy来生成代理对象的。JdkDynamicAopProxy、CglibAopProxy实现了AopProxy接口,如下:
 
1
2
3
4
5
6
7
public interface AopProxy {
 
    Object getProxy();
 
    Object getProxy(ClassLoader classLoader);
 
}

然后详细看下CglibProxy的代理对象的生成过程。CglibProxy、JdkDynamicAopProxy都拥有一个非常重要的属性AdvisedSupport advised这个属性包含了拦截的配置信息,这个属性在JdkDynamicAopProxy中已经说过了,不再详细说明。 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public Object getProxy(ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource());
        }
        try {
//此时的rootClass为BServiceImpl
            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方法(仅仅是打印出来警告信息,不做任何处理)
            // Validate the class, writing log messages as necessary.
            validateClassIfNecessary(proxySuperClass);
 
            // Configure 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));
 
            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);
 
            // Generate the proxy class and create a proxy instance.
            return createProxyClassAndInstance(enhancer, callbacks);
        }
        //略
    }

上述内容,就是使用Enhancer设置下要继承的父类、设置下要实现的接口、设置下回调然后就创建出代理对象。其中的一个重要的回调Callback为DynamicAdvisedInterceptor,在DynamicAdvisedInterceptor的intercept方法里面实现了和JDK动态代理同样类似的逻辑。 
接下来我们看下这一拦截过程是如何实现的。在DynamicAdvisedInterceptor的intercept方法里:
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
@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方法内容如下:
 
1
2
3
protected Object invokeJoinpoint() throws Throwable {
        return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
    }

就是利用反射进行目标方法的调用执行。 
再看下CglibMethodInvocation的invokeJoinpoint方法:
 
1
2
3
4
5
6
7
8
protected Object invokeJoinpoint() throws Throwable {
            if (this.publicMethod) {
                return this.methodProxy.invoke(this.target, this.arguments);
            }
            else {
                return super.invokeJoinpoint();
            }
        }

this.publicMethod就是说明所调用的方法是否是public类型的。我们来看下它的来历: 
1
2
3
4
5
6
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;
            this.publicMethod = Modifier.isPublic(method.getModifiers());
        }

在构建CglibMethodInvocation这个MethodInvocation时进行赋值的。Modifier.isPublic(method.getModifiers());就是判断该方法是否是public类型的。 
CglibMethodInvocation与ReflectiveMethodInvocation仅仅在执行目标方法的时候有所不同,当目标方法是public方法时,ReflectiveMethodInvocation一直采用反射的策略执行目标方法。而CglibMethodInvocation却使用this.methodProxy.invoke(this.target, this.arguments)代理方法来执行。看下它的好处的描述(CglibMethodInvocation的invokeJoinpoint()方法的注释):
 
1
2
3
4
5
6
7
8
/**
         * Gives a marginal performance improvement versus using reflection to
         * invoke the target when invoking public methods.
         */
        @Override
        protected Object invokeJoinpoint() throws Throwable {
//略
}

当执行public方法时,会比反射有一个更好的性能。然而当我们在使用cglib的callback的时候却还是使用反射,没有去使用MethodProxy。因此我们还是按照源码的使用方式来使用,来提升性能。 
本文章中许多步骤省略了,是因为在上一篇SpringAOP JDK的动态代理文章中都进行了详细介绍,同时许多的接口也在上上一篇文章SpringAOP的接口说明中给出了详细的说明。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
开发与运维
使用钉钉扫一扫加入圈子
+ 订阅

集结各类场景实战经验,助你开发运维畅行无忧

其他文章