对于springAOP的源码分析,我打算分三部分来讲解:1.配置文件的解析,解析为BeanDefination和其他信息然后注册到BeanFactory中;2.为目标对象配置增强行为以及代理对象的生成,可以理解为AOP的准备阶段;3.代理对象调用方法,增强行为的触发执行,此时是AOP生效的阶段。我们可以把1,2理解为IOC阶段;2,3理解为AOP阶段。

我们先看第一部分:BeanDefination的解析注册过程

由一个demo进入源码分析,创建一个接口UserDao

public interface UserDao {
void addUser();
void deleteUser();
}

创建UserDaoImpl类

public class UserDaoImpl implements UserDao{

    public void addUser() {
System.out.println("add user ");
} public void deleteUser() {
System.out.println("delete user ");
} }

创建一个Logger类

public class Logger {

    public void recordBefore(){
System.out.println("recordBefore");
} public void recordAfter(){
System.out.println("recordAfter");
} }

在aop.xml中添加配置信息

    <bean id="userDao" class="com.demo.aop.sourcecode.UserDaoImpl"/>

    <bean id="logger" class="com.demo.aop.sourcecode.Logger" />

    <!-- 切面:切入点和通知 -->
<aop:config>
<aop:aspect id="logger" ref="logger">
<aop:pointcut expression="execution(* com.demo.aop.sourcecode..*.*(..))" id="udpateUserMethod" />
<aop:before method="recordBefore" pointcut-ref="udpateUserMethod" />
<aop:after method="recordAfter" pointcut-ref="udpateUserMethod" />
</aop:aspect>
</aop:config>

写测试方法

@Test
public void testAop(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("aop.xml");//BeanDefination的解析注册,代理对象的生成
UserDao userDao = (UserDao) applicationContext.getBean("userDao");//可以看到userDao类型是以$Proxy开头的,说明是通过JDK动态代理的方式获取的
userDao.addUser();//增强行为发生的时刻
}

进入到AbstractApplicationContext类中的refresh方法

    public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh(); // Tell the subclass to refresh the internal bean factory.
       //BeanDefination的解析注册在这个方法中发生,进入这个方法
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory); try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory); // Initialize message source for this context.
initMessageSource(); // Initialize event multicaster for this context.
initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses.
onRefresh(); // Check for listener beans and register them.
registerListeners(); // Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event.
finishRefresh();
} catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
} // Destroy already created singletons to avoid dangling resources.
destroyBeans(); // Reset 'active' flag.
cancelRefresh(ex); // Propagate exception to caller.
throw ex;
} finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}

通过一步步追踪,我们可以进入DefaultBeanDefinitionDocumentReader类中的parseBeanDefinitions方法

    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//判断是否是默认的命名空间,默认的命名空间是 http://www.springframework.org/schema/beans
if (delegate.isDefaultNamespace(root)) {
       //获取所有的子节点,然后循环处理 
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
               //在aop.xml文件中,对userDao和logger的定义将在这里处理
parseDefaultElement(ele, delegate);
}
else {
               //对<aop:config>的定义在这里处理,因为它的命名空间是 http://www.springframework.org/schema/aop 进入该方法
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}

进入parseCustomElement方法,然后可以追踪到以下方法

    public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);
     //此处的handler为AopNamespaceHandler,接下来将用它对<aop:config>进行解析
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
//进入该方法
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

进入NamespaceHandlerSupport类中的parse方法

public BeanDefinition parse(Element element, ParserContext parserContext) {
return findParserForElement(element, parserContext).parse(element, parserContext);
}

由于<aop:config>的解析是由ConfigBeanDefinitionParser类来完成的,所以进入该类的parse方法,看解析过程

@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
CompositeComponentDefinition compositeDef =
new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
parserContext.pushContainingComponent(compositeDef); configureAutoProxyCreator(parserContext, element);
     //获取子节点,根据aop.xml文件中的配置,子节点为<aop:aspect>
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)) {
//进入该方法,解析<aop:aspect>
parseAspect(elt, parserContext);
}
} parserContext.popAndRegisterContainingComponent();
return null;
}

进入parseAspect方法

private void parseAspect(Element aspectElement, ParserContext parserContext) {
//获取定义的切面ID和ref
String aspectId = aspectElement.getAttribute(ID);
String aspectName = aspectElement.getAttribute(REF); try {
//将获取到的切面ID和ref封装到AspectEntry这个类中
this.parseState.push(new AspectEntry(aspectId, aspectName));
//把<aop:before>等通知相关的信息封装到AspectJPointcutAdvisor中,然后放到该集合里
List<BeanDefinition> beanDefinitions = new ArrayList<BeanDefinition>();
     //把ref相关的信息如aop.xml中的logger,updateUserMethod等封装到RunTimeBeanReference中,然后放到这个集合中
List<BeanReference> beanReferences = new ArrayList<BeanReference>(); List<Element> declareParents = DomUtils.getChildElementsByTagName(aspectElement, DECLARE_PARENTS);
for (int i = METHOD_INDEX; i < declareParents.size(); i++) {
Element declareParentsElement = declareParents.get(i);
beanDefinitions.add(parseDeclareParents(declareParentsElement, parserContext));
} // We have to parse "advice" and all the advice kinds in one loop, to get the
// ordering semantics right.
NodeList nodeList = aspectElement.getChildNodes();
boolean adviceFoundAlready = false;
//循环切面的子节点,然后判断是否是通知,然后进行对应的处理
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
if (isAdviceNode(node, parserContext)) {
if (!adviceFoundAlready) {
adviceFoundAlready = true;
if (!StringUtils.hasText(aspectName)) {
parserContext.getReaderContext().error(
"<aspect> tag needs aspect bean reference via 'ref' attribute when declaring advices.",
aspectElement, this.parseState.snapshot());
return;
}
//封装ref信息
beanReferences.add(new RuntimeBeanReference(aspectName));
}
//把通知相关信息封装到AspectJPointcutAdvisor这个类中,同时封装ref信息然后放到BeanReferences中
            //这个是解析通知的方法,可以进入看看
AbstractBeanDefinition advisorDefinition = parseAdvice(
aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
beanDefinitions.add(advisorDefinition);
}
}
       //把切面信息和通知信息封装到这个类中 
AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(
aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);
parserContext.pushContainingComponent(aspectComponentDefinition);
       //解析切入点,然后封装信息 
List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
for (Element pointcutElement : pointcuts) {
         //这个是具体解析切入点的方法
parsePointcut(pointcutElement, parserContext);
} parserContext.popAndRegisterContainingComponent();
}
finally {
this.parseState.pop();
}
}

其实我们可以看到,这个方法的目的就是解析<aop:aspect>中的配置信息然后封装到类中,最终都存放在了containingComponents这个栈中,方便后面使用,这就是整个解析过程。

在接下来的一篇博文中,我们讲解代理对象的生成,如何给目标对象配置增强行为的,也就是第二个阶段。

《springAOP源码分析二》

《springAOP源码分析三》

spring AOP源码分析(一)的更多相关文章

  1. spring AOP源码分析(三)

    在上一篇文章 spring AOP源码分析(二)中,我们已经知道如何生成一个代理对象了,那么当代理对象调用代理方法时,增强行为也就是拦截器是如何发挥作用的呢?接下来我们将介绍JDK动态代理和cglib ...

  2. Spring AOP 源码分析 - 拦截器链的执行过程

    1.简介 本篇文章是 AOP 源码分析系列文章的最后一篇文章,在前面的两篇文章中,我分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在我们的得 ...

  3. Spring AOP 源码分析 - 创建代理对象

    1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...

  4. Spring AOP 源码分析 - 筛选合适的通知器

    1.简介 从本篇文章开始,我将会对 Spring AOP 部分的源码进行分析.本文是 Spring AOP 源码分析系列文章的第二篇,本文主要分析 Spring AOP 是如何为目标 bean 筛选出 ...

  5. Spring AOP 源码分析系列文章导读

    1. 简介 前一段时间,我学习了 Spring IOC 容器方面的源码,并写了数篇文章对此进行讲解.在写完 Spring IOC 容器源码分析系列文章中的最后一篇后,没敢懈怠,趁热打铁,花了3天时间阅 ...

  6. Spring AOP源码分析(三):基于JDK动态代理和CGLIB创建代理对象的实现原理

    AOP代理对象的创建 AOP相关的代理对象的创建主要在applyBeanPostProcessorsBeforeInstantiation方法实现: protected Object applyBea ...

  7. 5.2 Spring5源码--Spring AOP源码分析二

    目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...

  8. 5.2 spring5源码--spring AOP源码分析二--切面的配置方式

    目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...

  9. spring aop 源码分析(三) @Scope注解创建代理对象

    一.源码环境的搭建: @Component @Scope(scopeName = ConfigurableBeanFactory.SCOPE_SINGLETON,proxyMode = ScopedP ...

  10. 最简 Spring AOP 源码分析!

    前言 最近在研究 Spring 源码,Spring 最核心的功能就是 IOC 容器和 AOP.本文定位是以最简的方式,分析 Spring AOP 源码. 基本概念 上面的思维导图能够概括了 Sprin ...

随机推荐

  1. 更高的压缩比,更好的性能–使用ORC文件格式优化Hive

    http://lxw1234.com/archives/2016/04/630.htm 关键字:orc.index.hive Hive从0.11版本开始提供了ORC的文件格式,ORC文件不仅仅是一种列 ...

  2. C#深度学习の枚举类型(IEnumerator,IEnumerable)

    一.关于枚举的含义 .Net提供了可枚举类型的接口IEnumerable和枚举器接口IEnumerator,程序集System.Collections 另: IQueryable 继承自IEnumer ...

  3. go语言中如何模拟100个IP同时并发访问服务器,每个ip要重复访问1000次。每个Ip一分钟之内只能访问一次

    package main import ( "time" "sync" "fmt" "sync/atomic" ) ty ...

  4. 5.04-requests_cookies

    import requests # 请求数据url member_url = 'https://www.yaozh.com/member/' headers = { 'User-Agent': 'Mo ...

  5. [Windows]查看运行进程的参数【wmic】

    参考  https://www.cnblogs.com/top5/p/3143827.html     和 https://blog.csdn.net/swazer_z/article/details ...

  6. 五、Oracle 分组查询、视图

    一.分组函数1.avg:平均分2.sum:求和3.max:最大值4.min:最小值注意:前面四个必须针对数值字段,且参数只能是一个5.count:求个数 二.分组查询1.语法是 group by 分组 ...

  7. 在使用 Spring Boot 和 MyBatis 动态切换数据源时遇到的问题以及解决方法

    相关项目地址:https://github.com/helloworlde/SpringBoot-DynamicDataSource 1. org.apache.ibatis.binding.Bind ...

  8. CF617E XOR and Favorite Number

    CF617E XOR and Favorite Number 已知一个序列 \(a_1,\ a_2,\ \cdots,\ a_n\) 和 \(k\) ,\(m\) 次询问给出 \(l,\ r\) ,求 ...

  9. Java NIO6:选择器1——理论篇

    一.选择器 选择器提供选择执行已经就绪的任务的能力,这使得多元I/O成为了可能,就绪执行和多元选择使得单线程能够有效地同时管理多个I/O通道. 某种程度上来说,理解选择器比理解缓冲区和通道类更困难一些 ...

  10. JPA和分布式事务简介

    1. Transaction 分两种,Local Transaction 和 Global Transaction. 涉及到一个Connection的Commit,称为Local Transactio ...