对于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. 现在使用Nginx实现TCP反向代理

    Nginx 在1.9.0版本发布以前如果要想做到基于TCP的代理及负载均衡需要通过打名为 nginx_tcp_proxy_module 的第三方patch来实现,该模块的代码托管在github上网址: ...

  2. node 打开浏览器

    npm install --save opn const opn = require('opn'); // 使用默认浏览器打开 // opn('http://sindresorhus.com'); / ...

  3. BSOJ 4062 -- 【清华集训2012】串珠子

    Description 铭铭有n个十分漂亮的珠子和若干根颜色不同的绳子.现在铭铭想用绳子把所有的珠子连接成一个整体. 现在已知所有珠子互不相同,用整数1到n编号.对于第i个珠子和第j个珠子,可以选择不 ...

  4. Django-rest-framework 接口实现 版本控制 versioning

    版本控制 rest_framework 提供了 5 种版本控制 以及对应的 写法 url的 更改都可以 在 from rest_framework import versioning 中查看 Acce ...

  5. Flex读取txt文件里的内容(一)

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/you23hai45/article/details/25248307  Flex读取txt文件里的内 ...

  6. 【JAVA8】双冒号

    现在JDK双冒号是: public class MyTest {     public static void  printValur(String str){         System.out. ...

  7. maven 标签classifier 研究一下

    研究一下maven的标签: <dependency> <groupId>io.netty</groupId> <artifactId>netty-tcn ...

  8. selenium之选项卡管理

    在使用selenium管理浏览器的时候遇到一些小问题,现将这些小问题作一下总结: (1)browser是什么? from selenium import webdriver browser = web ...

  9. Spring(1)_Bean初始化

    源码: 执行的代码 public static void main(String[] args) { ApplicationContext context=new ClassPathXmlApplic ...

  10. WPF Binding学习(三)

    转自;http://blog.csdn.net/lisenyang/article/details/18312199 1.控件与控件间的双向绑定 WPF还支持控件作为数据源, <TextBox ...