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的继承关系如下图所示:
以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对象,即其自身对象。可以用如下的序列图来表示:
五、总结
用一句话来形容Aop,那就是proxy+Interceptors+target,即一个代理对象,多个拦截器再加目标对象的技术。
附录:
1、《spring揭穿》。
如果你喜欢本文, 请长按二维码,关注公众号 分布式编程.
作者:分布式编程
出处:https://zthinker.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。