媒介
前面写了六篇文章具体地阐明白Spring Bean加载流程,这部门完了之后就要进入一个较量坚苦的部门了,就是AOP的实现道理阐明。为了探究AOP实现道理,首先界说几个类,一个Dao接口:
public interface Dao { public void select(); public void insert(); }
Dao接口的实现类DaoImpl:
public class DaoImpl implements Dao { @Override public void select() { System.out.println("Enter DaoImpl.select()"); } @Override public void insert() { System.out.println("Enter DaoImpl.insert()"); } }
界说一个TimeHandler,用于要领挪用前后打印时间,在AOP中,这饰演的是横切存眷点的脚色:
public class TimeHandler { public void printTime() { System.out.println("CurrentTime:" + System.currentTimeMillis()); } }
界说一个XML文件aop.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <bean id="daoImpl" class="org.xrq.action.aop.DaoImpl" /> <bean id="timeHandler" class="org.xrq.action.aop.TimeHandler" /> <aop:config proxy-target-class="true"> <aop:aspect id="time" ref="timeHandler"> <aop:pointcut id="addAllMethod" expression="execution(* org.xrq.action.aop.Dao.*(..))" /> <aop:before method="printTime" pointcut-ref="addAllMethod" /> <aop:after method="printTime" pointcut-ref="addAllMethod" /> </aop:aspect> </aop:config> </beans>
写一段测试代码TestAop.java:
public class TestAop { @Test public void testAop() { ApplicationContext ac = new ClassPathXmlApplicationContext("spring/aop.xml"); Dao dao = (Dao)ac.getBean("daoImpl"); dao.select(); } }
代码运行功效就不看了,有了以上的内容,我们就可以按照这些跟一下代码,看看Spring到底是如何实现AOP的。
AOP实现道理——找到Spring处理惩罚AOP的源头
有许多伴侣不肯意去看AOP源码的一个很大原因是因为找不到AOP源码实现的进口在那边,这个确实是。不外我们可以看一下上面的测试代码,就普通Bean也好、AOP也好,最终都是通过getBean要领获取到Bean并挪用要领的,getBean之后的工具已经前后都打印了TimeHandler类printTime()要领内里的内容,可以想见它们已经是被Spring容器处理惩罚过了。
既然如此,那无非就两个处所处理惩罚:
因此,本文环绕【1.加载Bean界说的时候应该有过非凡的处理惩罚】展开,先找一下到底是那边Spring对AOP做了非凡的处理惩罚。代码直接定位到DefaultBeanDefinitionDocumentReader的parseBeanDefinitions要领:
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); } }
正常来说,碰着<bean id=”daoImpl”…>、<bean id=”timeHandler”…>这两个标签的时候,城市执行第9行的代码,因为<bean>标签是默认的Namespace。可是在碰着后头的<aop:config>标签的时候就纷歧样了,<aop:config>并不是默认的Namespace,因此会执行第12行的代码,看一下:
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)); }
因为之前把整个XML理会为了org.w3c.dom.Document,org.w3c.dom.Document以树的形式暗示整个XML,详细到每一个节点就是一个Node。