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引入 ...
随机推荐
- [WinForm]dataGridView背景色交替
方法一: //设置表格背景色 dgvSaleOrder.RowsDefaultCellStyle.BackColor = Color.Ivory; //设置交替行的背景色 dgvSaleOrder.A ...
- 一篇详细的linux中shell语言的字符串处理
1 cut是以每一行为一个处理对象的,这种机制和sed是一样的.(关于sed的入门文章将在近期发布) 2 cut一般以什么为依据呢? 也就是说,我怎么告诉cut我想定位到的剪切内容呢? cut命令主要 ...
- Leetcode_80_Remove Duplicates from Sorted Array II
本文是在学习中的总结,欢迎转载但请注明出处:http://blog.csdn.net/pistolove/article/details/43835055 Follow up for "Re ...
- VC工程的.gitignore模板
VC工程的.gitignore模板 文件内容如下: #====================================== # .gitignore # # 2015-01-09 create ...
- Leetcode_198_House Robber
本文是在学习中的总结,欢迎转载但请注明出处:http://blog.csdn.net/pistolove/article/details/47680663 You are a professional ...
- Myexclipse创建Junit测试
. 下载JUnit的jar文件,下载地址在这里 2. 在MyEclipse中新建一个要测试的项目HelloJUnit 3. 添加一个要测试的类HelloJUnit,代码如下,注意需要先建package ...
- win7 64位下自行编译OpenCV2.4.10+CUDA toolkit 5.5的整个过程以及需要注意的问题(opencv+cuda并行计算元素的使用)
首先说明的是,这个帖子是成功的编译了dll,但是这个dll使用的时候还是很容易出现各种问题的. 发现错误可能是由于系统安装了太多版本的opencv,环境变量的设置混乱,造成dll版本加载 ...
- linux内核中访问共享资源
访问共享资源的代码区域称为临界区,临时以某种互斥机制加以保护.中断屏蔽.原子操作 自旋锁和信号量是Linux设备驱动中可采用的互斥途径. 在单CPU范围内避免竞态的一种简单方法是在进入临界区之前屏蔽系 ...
- BASE64 的加密与解密
package com.suning.security; import java.io.IOException; import sun.misc.BASE64Decoder; import sun.m ...
- IOS中的数据存储方式,特点,使用情况
数据存储的核心都是写文件,主要有四种持久化方式:属性列表(Plist),对象序列化,SQLite数据库,CoreData. 存储Plist: 键值进行存储,不能存储对象.对象需要序列化编码才能写入文件 ...