媒介
前面写了六篇文章具体地阐明白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。