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)); 	}