首先讲一下aop的概念,aop面向切面编程.我的理解是这是面向对象思想的一种进阶,把特定的逻辑与主逻辑分开处理,再通过一定的方式组合起来形成完整的处理过程.
下面说一下AOP的几个重要概念(都是我自己的理解,概念上肯定有偏差,但便于我记忆):
通知(advice):即要在主逻辑处运行的逻辑.我更喜欢另一个名字增强(《深入理解spring》中看到的).advice是个空接口,常用的实现由beforceAdvice、afterAdvice、ThrowsAdvice等
切点(pointcut):即要在哪里附加特定的逻辑,一般是特定的类或特定的方法上
连接点(joinpoint):即要在切点的哪个位置附加特定的逻辑,如方法前、方法后、抛出异常后等等
暂时想到这么多,以后想到再补充
OK,下面正式开始,首先先放一个aop的小demo
public class Car {
private Engine engine;
public void introduce(){
System.out.println("engine brand:" + engine.getBrandName());
}
public Engine getEngine() {
return engine;
}
public void setEngine(Engine engine) {
this.engine = engine;
}
}
//@Aspect
public class Myadvice {
//@Before("execution(* com.kevindai.aop.*.*(..))")
public void printSth(){
System.out.println("------------------bibibi-----------------");
}
}
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:annotation-config/>
<aop:aspectj-autoproxy/>
<bean name="car" class="com.kevindai.ioc.Car">
<property name="engine" ref="engine"/>
</bean>
<bean name="engine" class="com.kevindai.ioc.Engine">
<property name="brandName" value="bmw"/>
</bean>
<bean name="myadvice" class="com.kevindai.aop.Myadvice"></bean>
<!--需要用注解的同学请把下面一段注释,然后把Myadvice中的注解注释取消-->
<aop:config>
<aop:aspect ref="myadvice">
<aop:pointcut id="myAspect" expression="execution(* com.kevindai.aop.*.*(..))" />
<aop:before method="printSth" pointcut-ref="myAspect" />
<aop:before method="printSth" pointcut="execution(* com.kevindai.aop.*.*(..))" />
</aop:aspect>
</aop:config>
</beans>
public class AOPTest {
@SuppressWarnings("resource")
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("springioc.xml");
Car advice = (Car) ctx.getBean("car");
advice.introduce();
}
}
首先咱们是用applicationContext来初始化spring容器的,因此,当容器启动时咱们定义的bean就应该被初始化了,所以咱们先看看初始化时怎么对<aop:config></aop:config>中的内容进行处理;追踪的方法这里不再赘述,我贴出我认为比较关键的代码
解析<aop:config></aop:config>节点时会进入如下代码段,
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));
}
这里就是获取节点名,并进行解析,下面咱们看看解析的过程,追寻过程比较复杂,我不一一贴出,最终会到下面这段代码中
public BeanDefinition parse(Element element, ParserContext parserContext) {
CompositeComponentDefinition compositeDef =
new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
parserContext.pushContainingComponent(compositeDef);
configureAutoProxyCreator(parserContext, element);
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)) {
parseAspect(elt, parserContext);
}
}
parserContext.popAndRegisterContainingComponent();
return null;
}
这里其实主要是会对aop的个节点进行解析,如pointcut、advisor、aspect,因最外层节点是aspect,因此咱们进入parseAspect()看看
private void parseAspect(Element aspectElement, ParserContext parserContext) {
String aspectId = aspectElement.getAttribute(ID);
String aspectName = aspectElement.getAttribute(REF);
try {
this.parseState.push(new AspectEntry(aspectId, aspectName));
List<BeanDefinition> beanDefinitions = new ArrayList<BeanDefinition>();
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;
}
beanReferences.add(new RuntimeBeanReference(aspectName));
}
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();
}
}
想了一下,这里代码太复杂,等以后抽出整块的时间再仔细研究吧
//TODO