zoukankan      html  css  js  c++  java
  • AOP动态代理解析5-cglib代理的实现

    CGLIB是一个强大的高性能的代码生成包。它广泛地被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的Interception(拦截)。EasyMock和jMock是通过使用模仿(moke)对象来测试Java代码的包。它们都通过使用CGLIB来为那些没有接口的类创建模仿(moke)对象。

    CGLIB包的底层通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。除了CGLIB包,脚本语言例如Groovy和BeanShell,也是使用ASM来生成Java的字节码。当然不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

    CGLIB代理的类是委托给CglibAopProxy类去实现的。Cglib2AopProxy的入口是在getProxy,也就是说在CglibAopProxy类的getProxy方法中实现了Enhancer的创建及接口封装。

        public Object getProxy() {
            return getProxy(null);
        }
    
        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);
                    }
                }
                //验证Class
                validateClassIfNecessary(proxySuperClass);
                //创建及配置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 MemorySafeUndeclaredThrowableStrategy(UndeclaredThrowableException.class));
                enhancer.setInterceptDuringConstruction(false);
           //设置拦截器
                Callback[] callbacks = getCallbacks(rootClass);
                Class<?>[] types = new Class<?>[callbacks.length];
                for (int x = 0; x < types.length; x++) {
                    types[x] = callbacks[x].getClass();
                }
                enhancer.setCallbackFilter(new ProxyCallbackFilter(
                        this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
                enhancer.setCallbackTypes(types);
                enhancer.setCallbacks(callbacks);
                //生成代理类以及创建代理
                Object proxy;
                if (this.constructorArgs != null) {
                    proxy = enhancer.create(this.constructorArgTypes, this.constructorArgs);
                }
                else {
                    proxy = enhancer.create();
                }
    
                return proxy;
            }
            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) {
                throw new AopConfigException("Unexpected AOP exception", ex);
            }
        }

    以上函数完整地阐述了一个创建Spring中的Enhancer的过程,参考Enhancer的文档查看每个步骤的含义,这里最重要的是通过getCallbacks方法设置拦截器链。

        private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
            //对于expose-proxy属性的处理
            boolean exposeProxy = this.advised.isExposeProxy();
            boolean isFrozen = this.advised.isFrozen();
            boolean isStatic = this.advised.getTargetSource().isStatic();
            //将拦截器封装在DynamicAdvisedInterceptor中
            Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
            // Choose a "straight to target" interceptor. (used for calls that are
            // unadvised but can return this). May be required to expose the proxy.
            Callback targetInterceptor;
            if (exposeProxy) {
                targetInterceptor = isStatic ?
                        new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
                        new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource());
            }
            else {
                targetInterceptor = isStatic ?
                        new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
                        new DynamicUnadvisedInterceptor(this.advised.getTargetSource());
            }
            // Choose a "direct to target" dispatcher (used for
            // unadvised calls to static targets that cannot return this).
            Callback targetDispatcher = isStatic ?
                    new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp();
         //将拦截器链加入Callback中
            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)
            };
            Callback[] callbacks;
            // If the target is a static one and the advice chain is frozen,
            // then we can make some optimisations by sending the AOP calls
            // direct to the target using the fixed chain for that method.
            if (isStatic && isFrozen) {
                Method[] methods = rootClass.getMethods();
                Callback[] fixedCallbacks = new Callback[methods.length];
                this.fixedInterceptorMap = new HashMap<String, Integer>(methods.length);
    
                // TODO: small memory optimisation here (can skip creation for methods with no advice)
                for (int x = 0; x < methods.length; x++) {
                    List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(methods[x], rootClass);
                    fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
                            chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
                    this.fixedInterceptorMap.put(methods[x].toString(), x);
                }
                // Now copy both the callbacks from mainCallbacks
                // and fixedCallbacks into the callbacks array.
                callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
                System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);
                System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length);
                this.fixedInterceptorOffset = mainCallbacks.length;
            }
            else {
                callbacks = mainCallbacks;
            }
            return callbacks;
        }

    在getCallback中Spring考虑了很多情况,对于我们来说,只需要理解最常用的就可以了,比如将advised属性封装在DynamicAdvisedInterceptor并加入在callbacks中,这么做的目的是什么呢,如何调用呢?CGLIB中对于方法的拦截是通过将自定义的拦截器(实现MethodInterceptor接口)加入Callback中并在调用代理时直接激活拦截器中的intercept方法来实现的,那么在getCallback中正是实现了这样一个目的,DynamicAdvisedInterceptor继承自MethodInterceptor,加入Callback中后,在再次调用代理时会直接调用DynamicAdvisedInterceptor中的intercept方法,由此推断,对于CGLIB方式实现的代理,其核心逻辑必然在DynamicAdvisedInterceptor中的intercept中。

            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())) { //如果拦截器链为空则直接激活原方法 retVal = methodProxy.invoke(target, args); } else { //进入链 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方式实现代理中的invoke方法大同小异,都是首先构造链,然后封装此链进行串联调用,稍有些区别就是在JDK中直接构造ReflectiveMethodInvocation,而在cglib中使用CglibMethodInvocation。CglibMethodInvocation继承自ReflectiveMethodInvocation,但是proceed方法并没有重写。

     

     

     

     

     

     

     

     

  • 相关阅读:
    CSS浏览器兼容----IE的定制CSS样式
    CSS浏览器兼容---判断IE版本的HTM语句
    单链表操作1
    数学建模1
    浏览器内核学习笔记二
    浏览器内核学习笔记一
    网页使用特殊字体
    SQL Server 2008 R2没有卸载干净
    RadioButtonFor绑定值
    SVN 服务启动报错 0x8007042a
  • 原文地址:https://www.cnblogs.com/wade-luffy/p/6077494.html
Copyright © 2011-2022 走看看