为了账号安全,请及时绑定邮箱和手机立即绑定

spring aop & cglib 动态代理

标签:
Java

知识回顾

在文章初识AOP中,了解到为什么要用代理,静态代理和动态代理的区别,在JDK的动态代理中讲到JDK的动态代理是如何实现的,有什么优缺点,CGLIB中动态代理中我们又说明了CGLIB如何实现动态代理,和JDK动态代理有什么区别,同时我们在spring aop & jdk的动态代理中讲到了spring aop如何利用JDK的动态代理的,本篇将继续聊下Spring中的AOP是如何结合CGLIB的动态代理的

Spring AOP 示例

前提:先引入spring-context的jar包

同样的例子 定义一个IHello的接口和一个Hello的实现类如下

package seven.com.seven.aop;

public interface IHello {

    void say();

}

package seven.com.seven.aop;

public class Hello implements IHello {

    @Override
    public void say() {
        System.out.println("hello word");
    }
}

定义一个前置通知,一个后置通知,一个环绕通知,如下

package seven.com.seven.aop;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class BeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("------------------before------------------");
    }
}

package seven.com.seven.aop;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

public class AfterAdvice implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("------------------after------------------");
    }
}

package seven.com.seven.aop;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class SurroundAdvice implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
         System.out.println("------------------surround before---------------------");
         Object result = invocation.proceed();
         System.out.println("------------------surround after----------------------");
        return result;
    }
}

spring aop 实现动态代理

package seven.com.seven.aop;

import net.sf.cglib.core.DebuggingClassWriter;
import org.springframework.aop.framework.ProxyFactory;

public class DynamicSpringProxy {

    private Object target;

    public Object getProxy(Object target) {
        /**
         * 通过此语句可以把JDK动态代理 生成的class文件保存到磁盘,然后通过 反编译工具得到代码
         */
        //System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

        /**
         * 通过此语句可以把CGLIB动态代理 生成的class文件保存到磁盘,然后通过 反编译工具得到代码
         */
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");
        this.target = target;
        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(target);//让spring 走cglib的动态代理
        factory.addAdvice(new BeforeAdvice());
        factory.addAdvice(new AfterAdvice());
        factory.addAdvice(new SurroundAdvice());
        return factory.getProxy();
    }

}

package seven.com;

import seven.com.seven.aop.*;

public class App {
    public static void main(String[] args) {
        Hello hello = new Hello();
        IHello proxy = (IHello) new DynamicSpringProxy().getProxy(hello);
        proxy.say();
    }
}

运行结果

------------------before------------------
------------------surround before------------------
hello word
------------------surround after------------------
------------------after------------------

分析

前面的过程基本一样,根据选项选择使用JDK的动态代理还是CGLIB的动态代理,上一篇都讲过了,我们直接看下ObjenesisCglibAopProxy的getProxy方法

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);

            // 和之前讲到的CGLib一样,开始使用Enhancer 对象创建动态代理,有一点不一样就是CallBack是动态获取的,因为CallBack其实也就是MethodInterceptor,最终会执行MethodInterceptor的intercept方法
            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);
        }
    }

和之前讲到的CGLIB一样,使用Enhancer 对象创建动态代理,有一点不一样就是CallBack是动态获取的,因为CallBack其实也就是MethodInterceptor,最终会执行MethodInterceptor的intercept方法,看下获取callback的方法,在CglibAopProxy中有个getCallbacks:

private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
        // Parameters used for optimization choices...
        boolean exposeProxy = this.advised.isExposeProxy();
        boolean isFrozen = this.advised.isFrozen();
        boolean isStatic = this.advised.getTargetSource().isStatic();

        // Choose an "aop" interceptor (used for AOP calls).
        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[] 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 optimizations 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 optimization 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;
    }

最终要的就是 Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);了,这个MethodInterceptor就是后续代理类会执行的方法,看下其intercept如下,其实也是根据上面添加的Advice,生成对应的拦截器,然后使用责任链模式,循环调用拦截器而已,就连最终创建的MethodInvocation,其实创建的CglibMethodInvocation也是继承自ReflectiveMethodInvocation(我们上篇文章spring aop 使用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.
                    Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                    retVal = methodProxy.invoke(target, argsToUse);
                }
                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);
                }
            }
        }

总结

Spring AOP 不论选择使用JDK动态代理还是使用CGLIB做动态代理,最终都是根据我们之前添加的通知(Advice),来创建拦截器,然后根据连接器集合创建MethodInvocation,最终按照责任链模式,执行所有拦截器

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
JAVA开发工程师
手记
粉丝
5
获赞与收藏
27

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消