对于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. Kafka 0.11新功能介绍:空消费组延迟rebalance

    Kafka 0.11新功能介绍:空消费组延迟rebalance 在0.11之前的版本中,多个consumer实例加入到一个空消费组将导致多次的rebalance,这是由于每个consumer inst ...

  2. CentOS TinyProxy http(s)上网代理及置代理上网的方法

    http://blog.csdn.net/fwj380891124/article/details/42168683 http://computer.uoh.edu.cn/linux/2159.htm ...

  3. 数据库的连接、会话与SQLite

    通俗来讲,会话(Session) 是通信双方从开始通信到通信结束期间的一个上下文(Context).这个上下文是一段位于服务器端的内存:记录了本次连接的所有相关状态和运行数据. 连接(Connecti ...

  4. Anaconda的下载与安装

    1.下载地点: 支持国产:https://mirrors.tuna.tsinghua.edu.cn/ Anaconda官网:https://www.anaconda.com/download/ 2.下 ...

  5. Python 字典方法

    访问字典的值 字典中的 键/值 实际上就是一种映射关系,只要知道了 “键”,就肯定知道 “值”. >>> my_dict = dict(name = 'zhangsan',other ...

  6. 003_生成器(generator)内部解析

    #http://kb.cnblogs.com/page/87128/(未看完)

  7. ③---Java项目管理工具MAVEN安装与配置

    Java项目管理工具MAVEN安装配置以下将为大家介绍Java项目管理工具MAVEN安装及其配置. 一.下载MAVEN安装文件 maven下载地址:https://maven.apache.org/d ...

  8. 微信接入arduino

    https://blog.csdn.net/liudongdong19/article/details/81072857 一.准备工作.      1.微信公众号,个人的就可以了,不用企业号什么的.  ...

  9. 域名打开没有加上“http://”,导致报错{"code":-32603,"message":"Cannot navigate to invalid URL"}

    1.在robotframework中写用例 Open Browser  192.168.4.110:8880/jwzh  Chrome 2.没有写http:// 3.导致报错 4.正确写法应该是 Op ...

  10. 区别:ASP.NET MVC的Model、DTO、Command

    最近在用CQRS架构模式做项目,有些感悟,记录下来. 问题的描述(大家是否也存在过类似的情况呢?): 从刚开始时项目中没有区分这3种对象,所以导致了很多职责公用,然后就乱了,比如Command一部分职 ...