Spring系列之-Spring AOP设计原理(一)


声明:本文转载自https://my.oschina.net/wenbo123/blog/1594133,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

Spring AOP作为Spring中两大最要特性跟IOC一样重要,看了很多书籍,都没有把这个东西的来龙去脉讲清楚,网上很多文章标题也都是一知半解,甚至很多直接就切入到动态代理这块来讲。本文旨在从源码级别来分析Spring AOP的设计过程,用一个例子来从头到尾分析Spring AOP实现过程。

我们先上一个简单的例子:

public interface ShopService {      public String getShopName();  }   public class ShopServiceImpl implements ShopService{      public String getShopName() {         System.out.println("nike shop");         return "Nike";     }  }  public class TimeHandler {      public void printTime() {         System.out.println("当前时间:" + System.currentTimeMillis());     }  }  

XML配置:

<bean id="shopService" class="com.dianping.aop.ShopServiceImpl"/>     <bean id="timeHandler" class="com.dianping.aop.TimeHandler"/>      <aop:config proxy-target-class="true">         <aop:aspect id="time" ref="timeHandler">             <aop:pointcut id="point" expression="execution(* com.dianping.aop.ShopService.*(..))"/>             <aop:before method="printTime" pointcut-ref="point"/>             <aop:after method="printTime" pointcut-ref="point"/>         </aop:aspect>     </aop:config>

测试类:

public class Main {     public static void main(String[] args) {         BeanFactory beanFactory = new FileSystemXmlApplicationContext("classpath:appcontext.xml");         ShopService shopService = (ShopService) beanFactory.getBean("shopService");         shopService.getShopName();     } }

输出:

当前时间:1514042119262
nike shop
当前时间:1514042119283

可以看见在getShopName()方法执行前后都打印了当前时间,也就是执行了TimeHandler中的printTime()方法。

这是一个简单的AOP使用,下面将从这个例子触发解析Spring AOP设计实现。

可以发现打印时间是在getBean()方法之后执行的,说明AOP被Spring 容器处理过了,那么只有两个地方处理:

1.在Spring IOC容器初始化过程中处理。

2.在获取bean的过程中被处理。

前几篇文章分析过Spring IOC的初始化过程,在Spring IOC出事化过程中会调用XmlBeanDefinitionReader对XML文件中的Bean定义进行读取获取到Dom文档后进行解析,最终的解析BeanDefinition在DefaultBeanDefinitionDocumentReader的下述方法中。(本文源码均基于Spring 4.3.9)

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {         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)) { 						parseDefaultElement(ele, delegate); 					} 					else { 						delegate.parseCustomElement(ele); 					} 				} 			} 		} 		else { 			delegate.parseCustomElement(root); 		} 	}

在Spring IOC容器初始化过程中调用的是这句代码parseDefaultElement(ele, delegate),因为我们的bean都是通过<bean id="">声明的,这属于Spring默认命名,从上面配置Spring AOP XML中可以看出AOP的配置为<aop:>,这种配置不属于Spring默认命名,故不会执行parseDefaultElement(ele, delegate),而会执行delegate.parseCustomElement(ele),这句代码是解析自定义命名bean定义,<aop:>就属于自定义命名。

进一步进去:

public BeanDefinition parseCustomElement(Element ele) { 		return parseCustomElement(ele, null); 	}  	public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {         //获取命名空间并根据命名空间获取相应的处理 		String namespaceUri = getNamespaceURI(ele); 		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)); 	}

 

 

本文发表于2017年12月24日 10:32
(c)注:本文转载自https://my.oschina.net/wenbo123/blog/1594133,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 2256 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1