• 创建AOP代理(下篇)


    接着上篇文章就绪讲述创建AOP代理的CGLIB代理。

    CGLIB的使用示例

    CGLIB是一个强大的高性能的代码生成包。它广泛的被许多AOP的框架使用,例如SpringAOP和dynaop,为它们提供方法的拦截(Interception)。最流行的OR Mapping工具 Hibernate也使用CGLIB来代理单端single-ended(多对一和一对一)关联(对集合的延迟抓取是采用其它机制实现的)。EasyMock和jMock是通过使用模仿(moke)对象来测试Java代码的包。它们都是通过CGLIB来为那些没有接口的类创建模仿(moke)对象。

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

    先来了解下CGLIB的使用:

    public class EnhancerDemo {
    
        public static void main(String[] args){
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(EnhancerDemo.class);
            enhancer.setCallback(new MethodInterceptorImpl());
    
            EnhancerDemo demo = (EnhancerDemo) enhancer.create();
            demo.test();
            System.out.println(demo);
        }
    
        public void test(){
            System.out.println("EnhancerDemo test();");
        }
    
        private static class MethodInterceptorImpl implements MethodInterceptor{
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.err.println("Before invoke " + method);
                Object result = methodProxy.invokeSuper(o,objects);
                System.err.println("After invoke " + method);
                return result;
            }
        }
    }

    运行结果:

    EnhancerDemo test();
    com.joe.mytag.aop.cglib.EnhancerDemo$$EnhancerByCGLIB$$727abd04@20fa23c1
    Before invoke public void com.joe.mytag.aop.cglib.EnhancerDemo.test()
    After invoke public void com.joe.mytag.aop.cglib.EnhancerDemo.test()
    Before invoke public java.lang.String java.lang.Object.toString()
    Before invoke public native int java.lang.Object.hashCode()
    After invoke public native int java.lang.Object.hashCode()
    After invoke public java.lang.String java.lang.Object.toString()

    可以看到:System.out.println(demo);首先调用了toString方法,然后又调用了hashCode,生成的对象为EnhancerDemo$$EnhancerByCGLIB$$727abd04@20fa23c1的实例,这个类是CGLIB产生的。

    完成CGLIB代理的类是委托给CglibAopProxy类去实现的,我们进入这个类来一探究竟。

    按照前面的示例,我们可以判断出,CglibAopProxy的入口应该是getProxy,也就是说在CglibAopProxy类的getProxy方法中实现了Enhancer的创建及接口封装。

    public Object getProxy(@Nullable ClassLoader classLoader) {
            if (logger.isTraceEnabled()) {
                logger.trace("Creating CGLIB proxy: " + 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, classLoader);
    
                // 创建及配置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只在这里填充,在getCallbacks调用之后
                enhancer.setCallbackFilter(new ProxyCallbackFilter(
                        this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
                enhancer.setCallbackTypes(types);
    
                // 生成代理类并创建代理实例。
                return createProxyClassAndInstance(enhancer, callbacks);
            }
            catch (CodeGenerationException | IllegalArgumentException ex) {
                throw new AopConfigException("Could not generate CGLIB subclass of " + 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);
            }
        }

    上述方法完整的阐述了一个创建Spring的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 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<>(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;
        }

    在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;
                Object target = null;
                TargetSource targetSource = this.advised.getTargetSource();
                try {
                    if (this.advised.exposeProxy) {
                        // Make invocation available if necessary.
                        oldProxy = AopContext.setCurrentProxy(proxy);
                        setProxyContext = true;
                    }
                    // Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
                    target = targetSource.getTarget();
                    Class<?> targetClass = (target != null ? target.getClass() : null);
                    //获取拦截器链
                    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())) {
                        //如果拦截器为空则直接激活原方法
                        Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                        retVal = methodProxy.invoke(target, argsToUse);
                    }
                    else {
                        // 创建链
                        retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
                    }
                    retVal = processReturnType(proxy, target, method, retVal);
                    return retVal;
                }
                finally {
                    if (target != null && !targetSource.isStatic()) {
                        targetSource.releaseTarget(target);
                    }
                    if (setProxyContext) {
                        // 恢复到旧的代理
                        AopContext.setCurrentProxy(oldProxy);
                    }
                }
            }

    上述方法的实现与JDK Proxy中实现代理的中的invoke方法大同小异,都是首先构造链,然后封装此链进行串联调用,稍有区别的就是在JDK中直接构造函数ReflectiveMethodInvocation,而在cglib中使用CglibMethodInvocation,CglibMethodInvocation继承自ReflectiveMethodInvocation,但是process方法却并没有重写。

    参考:《Spring源码深度解析》 郝佳 编著:

    作者:Joe
    努力了的才叫梦想,不努力的就是空想,努力并且坚持下去,毕竟这是我相信的力量
  • 相关阅读:
    Trapping Rain Water
    Construct Binary Tree from Preorder and Inorder Traversal
    Flatten Binary Tree to Linked List
    Permutations II
    Unique Paths II
    Path Sum II
    Unique Binary Search Trees II
    evdev module-----uinput.py
    evdev module-----events.py
    evdev module-----device.py
  • 原文地址:https://www.cnblogs.com/Joe-Go/p/10238829.html
Copyright © 2020-2023  润新知