Spring AOP分析(3) -- CglibAopProxy实现AOP

上文探讨了应用JDK动态代理实现Spring AOP功能的方式,下面将继续探讨Spring AOP功能的另外一种实现方式 -- CGLIB。

首先,来看看类名CglibAopProxy,该类实现了两个接口:一个是AopProxy接口,一个是Serializable接口。直接忽略Serializable接口,AopProxy接口中只定义了两个同名方法getProxy。如下所示:

class CglibAopProxy implements AopProxy, Serializable

类中有一个非常重要的属性advised,该属性在JdkDynamicAopProxy中也存在,主要用于配置代理信息,是一个非常重要的属性,如下

protected final AdvisedSupport advised;

属性为final类型,可以被子类继承,一旦赋值后便不能改变。在CglibAopProxy中,advised在构造器中被赋值,后续不可更改:

    public CglibAopProxy(AdvisedSupport config) throws AopConfigException {
        Assert.notNull(config, "AdvisedSupport must not be null");
        if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
            throw new AopConfigException("No advisors and no TargetSource specified");
        }
        this.advised = config;
        this.advisedDispatcher = new AdvisedDispatcher(this.advised);
    }

跟JdkDynamicAopProxy一样,实现了AopProxy接口,实现了getProxy()方法,如下:

    @Override
    public Object getProxy(ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource());
        }

        try {
            Class<?> rootClass = this.advised.getTargetClass();
            Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

            Class<?> proxySuperClass = rootClass;
            if (ClassUtils.isCglibProxyClass(rootClass)) {
                proxySuperClass = rootClass.getSuperclass();
                Class<?>[] additionalInterfaces = rootClass.getInterfaces();
                for (Class<?> additionalInterface : additionalInterfaces) {
                    this.advised.addInterface(additionalInterface);
                }
            }

            // Validate the class, writing log messages as necessary.
            validateClassIfNecessary(proxySuperClass, classLoader);

            // 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 ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));

            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);
        }
        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 (Throwable ex) {
            // TargetSource.getTarget() failed
            throw new AopConfigException("Unexpected AOP exception", ex);
        }
    }

在创建代理过程中需要判断被代理类是否已经进行过验证,若没有验证过则执行验证。验证方法如下:

    private void doValidateClass(Class<?> proxySuperClass, ClassLoader proxyClassLoader, Set<Class<?>> ifcs) {
        if (proxySuperClass != Object.class) {
            Method[] methods = proxySuperClass.getDeclaredMethods();
            for (Method method : methods) {
                int mod = method.getModifiers();
                if (!Modifier.isStatic(mod)) {
                    if (Modifier.isFinal(mod)) {
                        if (implementsInterface(method, ifcs)) {
                            logger.warn("Unable to proxy interface-implementing method [" + method + "] because " +
                                    "it is marked as final: Consider using interface-based JDK proxies instead!");
                        }
                        logger.info("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.");
                    }
                    else if (!Modifier.isPublic(mod) && !Modifier.isProtected(mod) && !Modifier.isPrivate(mod) &&
                            proxyClassLoader != null && proxySuperClass.getClassLoader() != proxyClassLoader) {
                        logger.info("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.");
                    }
                }
            }
            doValidateClass(proxySuperClass.getSuperclass(), proxyClassLoader, ifcs);
        }
    }

接着配置CGLIB加强createEnhancer(),最后创建代理类生成代理实例createProxyClassAndInstance(enhancer, callbacks)

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏王磊的博客

JSON.stringify转换Date不正确的解決方法

JSON.stringify转换Date不正确的原因:国际时区(UTC)和中国时区(GMT)的原因,东八区+8等于国际时区。 解决方法,重新Es5的Date.p...

2604
来自专栏海说

Spring AOP分析(2) -- JdkDynamicAopProxy实现AOP

之前介绍了代理类是由默认AOP代理工厂DefaultAopProxyFactory中createAopProxy方法产生的。如果代理对象是接口类型,则生成Jdk...

3340
来自专栏IMWeb前端团队

关于时间的处理

计算机处理时间一直都是以时间戳的方式进行的, 并且以 格林尼治时间 1970/1/1 0:0:0 开始计算的: var date = new Date(0); ...

1980
来自专栏AhDung

js日期计算及快速获取周、月、季度起止日

机缘巧合,这段接触了一下js,刚开始各种磕碰各种不顺手,一个日期计算都折腾我半天,积累了一些,赶紧码下:  

802
来自专栏数据库

SQL Server 2014軟件下載及安裝教程

SQL Server 2014 32位 链接:http://pan.baidu.com/s/1gf2BBUj 密码:km87 SQL Server 2014 6...

2057
来自专栏

MorseCodeConverter 自用留存

 long[] pattern = MorseCodeConverter.pattern(text);  Vibrator vibrator = (Vibrat...

662
来自专栏一个会写诗的程序员的博客

JavaScript 获取 2 天之后的日期代码实例

741
来自专栏西安-晁州

nodejs时间工具类

/** * * @fmt 格式化字符串 * @Date 为需要格式化的日期 * * 示例:format(new Date(),'yyyy-MM-dd ...

1830
来自专栏大前端_Web

javascript Date format(js日期格式化)

722
来自专栏天天

日历案例

492

扫码关注云+社区