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揭穿》。
(转载本站文章请注明作者和出处 http://www.cnblogs.com/noahsark/ ,请勿用于任何商业用途)
Spring Aop技术原理分析的更多相关文章
- Spring AOP源码分析(三):基于JDK动态代理和CGLIB创建代理对象的实现原理
AOP代理对象的创建 AOP相关的代理对象的创建主要在applyBeanPostProcessorsBeforeInstantiation方法实现: protected Object applyBea ...
- Spring AOP 源码分析 - 拦截器链的执行过程
1.简介 本篇文章是 AOP 源码分析系列文章的最后一篇文章,在前面的两篇文章中,我分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在我们的得 ...
- Spring AOP 源码分析 - 创建代理对象
1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...
- Spring AOP源码分析--代理方式的选择
能坚持别人不能坚持的,才能拥有别人未曾拥有的.关注编程大道公众号,让我们一同坚持心中所想,一起成长!! 年前写了一个面试突击系列的文章,目前只有redis相关的.在这个系列里,我整理了一些面试题与大家 ...
- Spring框架系列(9) - Spring AOP实现原理详解之AOP切面的实现
前文,我们分析了Spring IOC的初始化过程和Bean的生命周期等,而Spring AOP也是基于IOC的Bean加载来实现的.本文主要介绍Spring AOP原理解析的切面实现过程(将切面类的所 ...
- Spring框架系列(10) - Spring AOP实现原理详解之AOP代理的创建
上文我们介绍了Spring AOP原理解析的切面实现过程(将切面类的所有切面方法根据使用的注解生成对应Advice,并将Advice连同切入点匹配器和切面类等信息一并封装到Advisor).本文在此基 ...
- Spring框架系列(11) - Spring AOP实现原理详解之Cglib代理实现
我们在前文中已经介绍了SpringAOP的切面实现和创建动态代理的过程,那么动态代理是如何工作的呢?本文主要介绍Cglib动态代理的案例和SpringAOP实现的原理.@pdai Spring框架系列 ...
- Spring框架系列(12) - Spring AOP实现原理详解之JDK代理实现
上文我们学习了SpringAOP Cglib动态代理的实现,本文主要是SpringAOP JDK动态代理的案例和实现部分.@pdai Spring框架系列(12) - Spring AOP实现原理详解 ...
- Spring AOP 实现原理
什么是AOP AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善.OOP引入 ...
随机推荐
- Useful Articles for the Oracle E-Business Suite Technology Stack for Technical Consultant / DBA [ID
Applies to: Oracle Applications Technology Stack - Version: 11.5.10.2 to 12.1.3 - Release: 11.5.10 ...
- 【Unity插件】LitJson杂谈
距离上一次更新博客已有一段时间了,一实习就懒了,嘿嘿.这次谈一下在实习里新碰到的一个Unity插件--LitJson(也可以去官网下载最新版). 开场白 LitJson是一个开源项目,比较小巧轻便,安 ...
- studio多渠道打包
由于国内Android市场众多渠道,为了统计每个渠道的下载及其它数据统计,就需要我们针对每个渠道单独打包,如果让你打几十个市场的包岂不烦死了,不过有了Gradle,这事就简单了. 友盟多渠道打包 废话 ...
- Graph Cut and Its Application in Computer Vision
Graph Cut and Its Application in Computer Vision 原文出处: http://lincccc.blogspot.tw/2011/04/graph-cut- ...
- UML之概述
UML,英文名曰:Unified Modeling Language,她还有个中文名字叫统一建模语言,简单的来说,她就是一种绘制软件蓝图的标准语言.她的表达能力特别强,可以描述开 ...
- Gradle 1.12用户指南翻译——第四十二章. Announce插件
本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...
- butternife Zelezny自动注入插件
插件地址:http://plugins.jetbrains.com/plugin/7369 Products: IntelliJ IDEA, RubyMine, WebStorm, PhpStorm, ...
- java时间操作
这篇讲的也很专业:http://soft.zdnet.com.cn/software_zone/2007/1129/660028.shtml java中的时间操作不外乎这四种情况: 1.获取当前时间 ...
- javascript访问html元素的内容(2)
对于(1)中最后一个包装方式创建的是一个方法,我们必须以方法调用的方式来使用它,这和其他默认的以属性返回结果略有不同,如果有强迫症的童鞋有些伤不起,那么我们下面就把它实现为属性返回的方式: //chi ...
- Kafka消费者-从Kafka读取数据
(1)Customer和Customer Group (1)两种常用的消息模型 队列模型(queuing)和发布-订阅模型(publish-subscribe). 队列的处理方式是一组消费者从服务器读 ...