zoukankan      html  css  js  c++  java
  • Spring Aop技术原理分析

    本篇文章从Aop xml元素的解析开始,分析了Aop在Spring中所使用到的技术。包括Aop各元素在容器中的表示方式、Aop自动代理的技术、代理对象的生成及Aop拦截链的调用等等。将这些技术串联起来,就能勾勒出Aop在Spring中的使用脉络。

    一、Spring Aop的解析

      在Spring xml配置中,不同的功能配置通过不同的命名空间引入,如事务方面的配置通过引入http://www.springframework.org/schema/tx命名空间来实现,而对Aop的配置则是引入http://www.springframework.org/schema/aop。对于引入的这些命名空间及其元素,Spring注册了不同的NamespaceHandler类型来处理,如Aop配置元素的解析就是通过AopNamespaceHandler来实现。在Spring中,各种NamespaceHandler的继承关系如下图所示:

    image

    以Aop配置信息为例,对于该命名空间各元素的解析主要是通过AopNamespaceHandler来注册,每一个元素对应一个解析器,其源码如下所示:

    复制代码
    public void init() {
            // In 2.0 XSD as well as in 2.1 XSD.
            registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
            registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
            registerBeanDefinitionDecorator("scoped-proxy", new copedProxyBeanDefinitionDecorator());
    
            // Only in 2.0 XSD: moved to context namespace as of 2.1
            registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
        }
    复制代码

    如果在xml配置文件中配置了<aop:aspectj-autoproxy />元素,则是通过SpringConfiguredBeanDefinitionParser类来解析。Spring将所要用到的handler分别配置在jar包的META-INF/ spring.handlers文件中,spring-aop包中的文件信息如下:

    http://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler

    对spring.handlers文件的加载则是在DefaultNamespaceHandlerResolver中,

    复制代码
    private Map<String, Object> getHandlerMappings() {
        if (this.handlerMappings == null) {
            synchronized (this) {
            if (this.handlerMappings == null) {
            try {
                Properties mappings =PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
                Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(mappings.size());
                CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
                his.handlerMappings = handlerMappings;
            }
            catch (IOException ex) {
            }
            }
        }
        }
            return this.handlerMappings;
    }
    复制代码

    其中handlerMappingsLocation为META-INF/spring.handlers。DefaultNamespaceHandlerResolver类存放在XmlReaderContext类中,由BeanDefinitionParserDelegate和BeanDefinitionParserDelegate调用,而这两个则在DefaultBeanDefinitionDocumentReader类中使用,最终由各种BeanFarctory(Spring容器)来调用。

    二、注册AutoProxyCreator类

    针对@AspectJ风格的AOP,Spring Aop提供了两个AutoProxyCreator实现类进行自动代理,分别是AspectJAwareAdvisorAutoProxyCreator和AnnotationAwareAspectJAutoProxyCreator,对应于配置文件(<aop:config>)和使用注解的方式。在配置文件中加入<aop:aspect-autoproxy/>,spring会使用AnnotationAwareAspectJAutoProxyCreator,而加入<aop:config />则会使用AspectJAwareAdvisorAutoProxyCreator。在Spring内部,只会使用其中之一。如果两种方式都配置了,则会使用AnnotationAwareAspectJAutoProxyCreator(在spring内部注解方式的优先级更高),详情可以查看AopConfigUtils类。因为AnnotationAwareAspectJAutoProxyCreator继承于AspectJAwareAdvisorAutoProxyCreator ,在调用自己的处理逻辑之前,会调用父类的实现逻辑,所以前者兼容后者。

    三、Aop代理对象的生成

    通过配置文件(或注解)将Aop切面配置好之后,ConfigBeanDefinitionParser类会将pointcut,advisor和aspect解析生成BeanDefinition,并注册到相应的BeanFactory中,以便在AspectJAwareAdvisorAutoProxyCreator中使用,解析的代码如下:

    复制代码
    public BeanDefinition parse(Element element, ParserContext parserContext) {
            CompositeComponentDefinition compositeDef =
                    new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
            parserContext.pushContainingComponent(compositeDef);
    
            configureAutoProxyCreator(parserContext, element);
    
            List<Element> childElts = DomUtils.getChildElements(element);
            for (Element elt: childElts) {
                String localName = parserContext.getDelegate().getLocalName(elt);
                if (POINTCUT.equals(localName)) {
                    parsePointcut(elt, parserContext);
                }
                else if (ADVISOR.equals(localName)) {
                    parseAdvisor(elt, parserContext);
                }
                else if (ASPECT.equals(localName)) {
                    parseAspect(elt, parserContext);
                }
            }
    
            parserContext.popAndRegisterContainingComponent();
            return null;
        }
    复制代码

    每当读取到aop:config元素时,spring会将其子元素分别解析并注册到BeanFactory中。当调用getBean()时,BeanFactory会调用注册到容器中的BeanPostProcessor(AspectJAwareAdvisorAutoProxyCreator)对象,判断是否满足拦截请求,如果满足,则获取所有满足条件的Advisor,加入到ProxyFactory中,生成代理对象返回,其代码如下所示:

    复制代码
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
            
            // Create proxy if we have advice.
            Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
            if (specificInterceptors != DO_NOT_PROXY) {
                this.advisedBeans.put(cacheKey, Boolean.TRUE);
                Object proxy = createProxy(bean.getClass(), beanName, 
                    pecificInterceptors, new SingletonTargetSource(bean));
                this.proxyTypes.put(cacheKey, proxy.getClass());
                return proxy;
            }
    
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
    }
    复制代码

    在BeanFactory生成Bean对象的时候,会对Bean对象进行一些初始化操作,1)判断是否继承aware接口,然后注入相应的aware对象;2)调用BeanPostProcessor类中的postProcessBeforeInitialization方法;3)判断是否继承InitializingBean,然后afterPropertiesSet方法;4),调用BeanPostProcessor类中的postProcessAfterInitialization方法。对代理对象的生成主要是在第2和第4步,其代码如下所示:

    复制代码
    protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
            if (System.getSecurityManager() != null) {
                AccessController.doPrivileged(new PrivilegedAction<Object>() {
                    public Object run() {
                        invokeAwareMethods(beanName, bean);
                        return null;
                    }
                }, getAccessControlContext());
            }
            else {
                // step 1
                invokeAwareMethods(beanName, bean);
            }
    
            Object wrappedBean = bean;
            if (mbd == null || !mbd.isSynthetic()) {
                // step 2
                wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
            }
    
            try {
                // step 3
                invokeInitMethods(beanName, wrappedBean, mbd);
            }
            catch (Throwable ex) {
                throw new BeanCreationException(
                        (mbd != null ? mbd.getResourceDescription() : null),
                        beanName, "Invocation of init method failed", ex);
            }
    
            if (mbd == null || !mbd.isSynthetic()) {
                // step 4
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
            }
            return wrappedBean;
        }
    复制代码

    在Spring容器中有可能会注册多个BeanPostProcessor,执行第2和第4步时,它会返回第一个不为空的对象,这时起作用的只有一个BeanPostProcessor对象,所以在注册自动代理对象的时候要尤为注意,其代码如下所示:

    复制代码
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
    
        Object result = existingBean;
        for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
            result = beanProcessor.postProcessAfterInitialization(result, beanName);
            if (result == null) {
                return result;
            }
        }
        return result;
    }
    复制代码

    四、Aop拦截链的调用

    在代理对象中,有可能会有多个Advisor对象与之匹配,这些Advisor会在最终目标对象执行之前,按照指定的顺序和优先级执行,顺序号决定优先级,顺序号越小,优先级越高,优先级排在前面的,将被优先执行。默认情况下,如果不明确指定各个Advisor的执行顺序,那么Spring会按照它们的声明顺序应用他们,最先声明的顺序号最小但优先级最大,其次将之。顺序号由Ordered指定,在BeanPostProcessor 各个子类中实现排序,如AspectJAwareAdvisorAutoProxyCreator中的sortAdvisors函数。

    代理对象中会存有一个Advisor列表,以JdkDynamicAopProxy(CglibAopProxy类似)为例,它实现了InvocationHandler接口,并将自己传给了代理对象,在代理对象中会调用其invoke方法,在该方法中有一段关键代码:

    复制代码
    // 得到这个方法的拦截器链
    List<Object> chain = 
    this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    
    // 判断拦截器链是否为空,若为空,直接调用目标方法
    if (chain.isEmpty()) {
        // 直接调用目标方法
        retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
    }
    else {
        // 调用拦截器链
        invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
        retVal = invocation.proceed();
    }
    复制代码

    调用拦截器链的功能主要ReflectiveMethodInvocation类中的proceed方法来实现,代码如下所示:

    复制代码
    public Object proceed() throws Throwable {
            // 判断是否到了链尾,如果是则执行目标方法,调用结束。
            if (this.currentInterceptorIndex == 
                               this.interceptorsAndDynamicMethodMatchers.size() - 1) {
                return invokeJoinpoint();
            }
            // 取出当前的Advisor;
            Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
            if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
                // 判断是否需要动态判断参数,如果需要则执行如下操作;
           // 参数验证通过则执行Advisor,否则跳过该Advisor;
                InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
                if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
                    return dm.interceptor.invoke(this);
                }
                else {
                    return proceed();
                }
            }
            else {
            // 如果不需要动态判断参数,则执行该Advisor,因为在之前已经验证通过了;
            return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
            }
        }
    复制代码

    在上面的代码中,好像并没有类似递归操作的语句(也没有循环语句)来执行拦截器链,那么代码是怎么执行多个Advisor的?Spring对三种Advisor(MethodBeforeAdvice,AfterReturningAdvice和ThrowsAdvice)采用了适配器方式,将它们转换为MethodInterceptor方法,如MethodBeforeAdviceAdapter实现如下:

    复制代码
    class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
    
        public boolean supportsAdvice(Advice advice) {
            return (advice instanceof MethodBeforeAdvice);
        }
    
        public MethodInterceptor getInterceptor(Advisor advisor) {
            MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
            return new MethodBeforeAdviceInterceptor(advice);
        }
    }
    复制代码

    MethodBeforeAdvice的语义是指在目标方法之前执行,在MethodBeforeAdviceInterceptor类按照其语义进行了转义,使得在ReflectiveMethodInvocation类中可以统一用invoke方法进行调用,其invoke方法代码如下所示:

    public Object invoke(MethodInvocation mi) throws Throwable {
            this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
            return mi.proceed();
        }

    对比BeforeAdvice的适配,现在应该可以想像AfterReturningAdvice的适配了,那就是先执行mi.proceed()方法,然后再执行advice的after方法,用代码来验证一下:

    public Object invoke(MethodInvocation mi) throws Throwable {
            Object retVal = mi.proceed();
            this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
            return retVal;
        }

    回到刚才的问题,代码是怎么完成递归操作的?看了代码,应该很清楚了。在MethodBeforeAdviceInterceptor的invoke方法中,有mi.proceed()这样的语句,而mi则是由ReflectiveMethodInvocation传入的this对象,即其自身对象。可以用如下的序列图来表示:

    image

    五、总结

    用一句话来形容Aop,那就是proxy+Interceptors+target,即一个代理对象,多个拦截器再加目标对象的技术。

    附录:

    1、《spring揭穿》。

  • 相关阅读:
    1058 A+B in Hogwarts (20)
    1036. Boys vs Girls (25)
    1035 Password (20)
    1027 Colors in Mars (20)
    1009. Product of Polynomials (25)
    1006. Sign In and Sign Out
    1005 Spell It Right (20)
    1046 Shortest Distance (20)
    ViewPager页面滑动,滑动到最后一页,再往后滑动则执行一个事件
    IIS7.0上传文件限制的解决方法
  • 原文地址:https://www.cnblogs.com/daichangya/p/12958451.html
Copyright © 2011-2022 走看看