spring aop

Posted zhenhong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring aop相关的知识,希望对你有一定的参考价值。

 

直接找到解析aop标签的方法:

 

 1 protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
 2         if (delegate.isDefaultNamespace(root)) {
 3             NodeList nl = root.getChildNodes();
 4             for (int i = 0; i < nl.getLength(); i++) {
 5                 Node node = nl.item(i);
 6                 if (node instanceof Element) {
 7                     Element ele = (Element) node;
 8                     if (delegate.isDefaultNamespace(ele)) {
 9                         parseDefaultElement(ele, delegate);
10                     }
11                     else {
12                         delegate.parseCustomElement(ele);
13                     }
14                 }
15             }
16         }
17         else {
18             delegate.parseCustomElement(root);
19         }
20     }

 

由于aop属于自定义标签,所以它会执行第12行的代码。

1 public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
2         String namespaceUri = getNamespaceURI(ele);
3         NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
4         if (handler == null) {
5             error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
6             return null;
7         }
8         return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
9     }

通过aop标签获取到它的命名空间uri,通过命名空间去找到对应的命名空间处理器,这个处理器的定义在springaop包下的一个叫spring.handlers的文件里声明了,它的内容是这样的

 

http\\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler

 

spring就是通过这个声明去加载这个aop命名空间处理器,通过反射的方式构建对象。构建好对象后调用它的parse方法

 

 1 public BeanDefinition parse(Element element, ParserContext parserContext) {
 2         CompositeComponentDefinition compositeDef =
 3                 new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
 4         parserContext.pushContainingComponent(compositeDef);
 5 
 6         configureAutoProxyCreator(parserContext, element);
 7 
 8         List<Element> childElts = DomUtils.getChildElements(element);
 9         for (Element elt: childElts) {
10             String localName = parserContext.getDelegate().getLocalName(elt);
11             if (POINTCUT.equals(localName)) {
12                 parsePointcut(elt, parserContext);
13             }
14             else if (ADVISOR.equals(localName)) {
15                 parseAdvisor(elt, parserContext);
16             }
17             else if (ASPECT.equals(localName)) {
18                 parseAspect(elt, parserContext);
19             }
20         }
21 
22         parserContext.popAndRegisterContainingComponent();
23         return null;
24     }

 

第6行的代码配置了一个org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator类,像前面的bean一样被定义成了一个BeanDefinition对象保存到了BeanFactory的beanDefinitionMap中。这个AspectJAwareAdvisorAutoProxyCreator是用来干什么的呢?看到后面才知道。

第8行获取到了这个aop:config下的子元素

是pointcut标签就处理pointcut,是advisor就处理advisor

看到第18行我们配置了一个切面,所以spring会解析这个aspect标签

 1 private void parseAspect(Element aspectElement, ParserContext parserContext) {
 2         String aspectId = aspectElement.getAttribute(ID);
 3         String aspectName = aspectElement.getAttribute(REF);
 4 
 5         try {
 6             this.parseState.push(new AspectEntry(aspectId, aspectName));
 7             List<BeanDefinition> beanDefinitions = new ArrayList<BeanDefinition>();
 8             List<BeanReference> beanReferences = new ArrayList<BeanReference>();
 9 
10             List<Element> declareParents = DomUtils.getChildElementsByTagName(aspectElement, DECLARE_PARENTS);
11             for (int i = METHOD_INDEX; i < declareParents.size(); i++) {
12                 Element declareParentsElement = declareParents.get(i);
13                 beanDefinitions.add(parseDeclareParents(declareParentsElement, parserContext));
14             }
15 
16             // We have to parse "advice" and all the advice kinds in one loop, to get the
17             // ordering semantics right.
18             NodeList nodeList = aspectElement.getChildNodes();
19             boolean adviceFoundAlready = false;
20             for (int i = 0; i < nodeList.getLength(); i++) {
21                 Node node = nodeList.item(i);
22                 if (isAdviceNode(node, parserContext)) {
23                     if (!adviceFoundAlready) {
24                         adviceFoundAlready = true;
25                         if (!StringUtils.hasText(aspectName)) {
26                             parserContext.getReaderContext().error(
27                                     "<aspect> tag needs aspect bean reference via \'ref\' attribute when declaring advices.",
28                                     aspectElement, this.parseState.snapshot());
29                             return;
30                         }
31                         beanReferences.add(new RuntimeBeanReference(aspectName));
32                     }
33                     AbstractBeanDefinition advisorDefinition = parseAdvice(
34                             aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
35                     beanDefinitions.add(advisorDefinition);
36                 }
37             }
38 
39             AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(
40                     aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);
41             parserContext.pushContainingComponent(aspectComponentDefinition);
42 
43             List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
44             for (Element pointcutElement : pointcuts) {
45                 parsePointcut(pointcutElement, parserContext);
46             }
47 
48             parserContext.popAndRegisterContainingComponent();
49         }
50         finally {
51             this.parseState.pop();
52         }
53     }

第22行表示解析在aspect标签下的通知,假如此时读取到了一个before标签,开始解析这个通知

 1 private AbstractBeanDefinition parseAdvice(
 2             String aspectName, int order, Element aspectElement, Element adviceElement, ParserContext parserContext,
 3             List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {
 4 
 5         try {
 6             this.parseState.push(new AdviceEntry(parserContext.getDelegate().getLocalName(adviceElement)));
 7 
 8             // create the method factory bean
 9             RootBeanDefinition methodDefinition = new RootBeanDefinition(MethodLocatingFactoryBean.class);
10             methodDefinition.getPropertyValues().add("targetBeanName", aspectName);
11             methodDefinition.getPropertyValues().add("methodName", adviceElement.getAttribute("method"));
12             methodDefinition.setSynthetic(true);
13 
14             // create instance factory definition
15             RootBeanDefinition aspectFactoryDef =
16                     new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);
17             aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName);
18             aspectFactoryDef.setSynthetic(true);
19 
20             // register the pointcut
21             AbstractBeanDefinition adviceDef = createAdviceDefinition(
22                     adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef,
23                     beanDefinitions, beanReferences);
24 
25             // configure the advisor
26             RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class);
27             advisorDefinition.setSource(parserContext.extractSource(adviceElement));
28             advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef);
29             if (aspectElement.hasAttribute(ORDER_PROPERTY)) {
30                 advisorDefinition.getPropertyValues().add(
31                         ORDER_PROPERTY, aspectElement.getAttribute(ORDER_PROPERTY));
32             }
33 
34             // register the final advisor
35             parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);
36 
37             return advisorDefinition;
38         }
39         finally {
40             this.parseState.pop();
41         }
42     }

第九行创建了一个方法工厂bean,MethodLocatingFactoryBean这个类有targetBeanName,methodName,method三个属性,method是methodName的对应Method对象。

第10行给methodDefinition设置属性targetBeanName为切面类的beanName,也就是id

第11行给methodDefinition设置属性methodName,表示这个通知要切入的方法名。

 

比如:

1 <aop:config>
2         <aop:aspect id="aspect" ref="aspectID">
3             <aop:pointcut expression="execution(* com.test.*.*(..))"
4                 id="cutpoint" />
5             <aop:before method="before" pointcut-ref="cutpoint" />
6             
7         </aop:aspect>
8 
9     </aop:config>

那么targetBeanName为aspectID,methodName为before。

第16行创建了一个实例工厂bean,SimpleBeanFactoryAwareAspectInstanceFactory这个类除了父类的具有两个属性aspectBeanName,beanFactory,aspectBeanName指的切面的beanName,也就是上面的aspectID,beanFactory指的BeanFactory的实例

第17行给这个SimpleBeanFactoryAwareAspectInstanceFactory定义属性,aspectBeanName为aspectID

进入第21行

 1 private AbstractBeanDefinition createAdviceDefinition(
 2             Element adviceElement, ParserContext parserContext, String aspectName, int order,
 3             RootBeanDefinition methodDef, RootBeanDefinition aspectFactoryDef,
 4             List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {
 5 
 6         RootBeanDefinition adviceDefinition = new RootBeanDefinition(getAdviceClass(adviceElement, parserContext));
 7         adviceDefinition.setSource(parserContext.extractSource(adviceElement));
 8 
 9         adviceDefinition.getPropertyValues().add(ASPECT_NAME_PROPERTY, aspectName);
10         adviceDefinition.getPropertyValues().add(DECLARATION_ORDER_PROPERTY, order);
11 
12         if (adviceElement.hasAttribute(RETURNING)) {
13             adviceDefinition.getPropertyValues().add(
14                     RETURNING_PROPERTY, adviceElement.getAttribute(RETURNING));
15         }
16         if (adviceElement.hasAttribute(THROWING)) {
17             adviceDefinition.getPropertyValues().add(
18                     THROWING_PROPERTY, adviceElement.getAttribute(THROWING));
19         }
20         if (adviceElement.hasAttribute(ARG_NAMES)) {
21             adviceDefinition.getPropertyValues().add(
22                     ARG_NAMES_PROPERTY, adviceElement.getAttribute(ARG_NAMES));
23         }
24 
25         ConstructorArgumentValues cav = adviceDefinition.getConstructorArgumentValues();
26         cav.addIndexedArgumentValue(METHOD_INDEX, methodDef);
27 
28         Object pointcut = parsePointcutProperty(adviceElement, parserContext);
29         if (pointcut instanceof BeanDefinition) {
30             cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcut);
31             beanDefinitions.add((BeanDefinition) pointcut);
32         }
33         else if (pointcut instanceof String) {
34             RuntimeBeanReference pointcutRef = new RuntimeBeanReference((String) pointcut);
35             cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcutRef);
36             beanReferences.add(pointcutRef);
37         }
38 
39         cav.addIndexedArgumentValue(ASPECT_INSTANCE_FACTORY_INDEX, aspectFactoryDef);
40 
41         return adviceDefinition;
42     }

第6行创建了一个关于advice的BeanDefinition。首先它调用了getAdviceClass方法,我们进去看看

 1 private Class<?> getAdviceClass(Element adviceElement, ParserContext parserContext) {
 2         String elementName = parserContext.getDelegate().getLocalName(adviceElement);
 3         if (BEFORE.equals(elementName)) {
 4             return AspectJMethodBeforeAdvice.class;
 5         }
 6         else if (AFTER.equals(elementName)) {
 7             return AspectJAfterAdvice.class;
 8         }
 9         else if (AFTER_RETURNING_ELEMENT.equals(elementName)) {
10             return AspectJAfterReturningAdvice.class;
11         }
12         else if (AFTER_THROWING_ELEMENT.equals(elementName)) {
13             return AspectJAfterThrowingAdvice.class;
14         }
15         else if (AROUND.equals(elementName)) {
16             return AspectJAroundAdvice.class;
17         }
18         else {
19             throw new IllegalArgumentException("Unknown advice kind [" + elementName + "].");
20         }
21     }

看见了吗?根据通知的类型返回相应的通知类,before对应AspectJMethodBeforeAdvice类,after对应AspectJAfterAdvice类,before和after对应的通知类的内部结构有些区别,before对应的类有before方法,但after对应的通知类是没有after方法的,代替使用的是一个invoke方法。现在不深入探讨它们是干什么的,到了调用通知方法的时候自然就明白了,这里先不管。

我们再次回到createAdviceDefinition方法

 1 private AbstractBeanDefinition createAdviceDefinition(
 2             Element adviceElement, ParserContext parserContext, String aspectName, int order,
 3             RootBeanDefinition methodDef, RootBeanDefinition aspectFactoryDef,
 4             List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {
 5 
 6         RootBeanDefinition adviceDefinition = new RootBeanDefinition(getAdviceClass(adviceElement, parserContext));
 7         adviceDefinition.setSource(parserContext.extractSource(adviceElement));
 8 
 9         adviceDefinition.getPropertyValues().add(ASPECT_NAME_PROPERTY, aspectName);
10         adviceDefinition.getPropertyValues().add(DECLARATION_ORDER_PROPERTY, order);
11 
12         if (adviceElement.hasAttribute(RETURNING)) {
13             adviceDefinition.getPropertyValues().add(
14                     RETURNING_PROPERTY, adviceElement.getAttribute(RETURNING));
15         }
16         if (adviceElement.hasAttribute(THROWING)) {
17             adviceDefinition.getPropertyValues().add(
18                     THROWING_PROPERTY, adviceElement.getAttribute(THROWING));
19         }
20         if (adviceElement.hasAttribute(ARG_NAMES)) {
21             adviceDefinition.getPropertyValues().add(
22                     ARG_NAMES_PROPERTY, adviceElement.getAttribute(ARG_NAMES));
23         }
24 
25         ConstructorArgumentValues cav = adviceDefinition.getConstructorArgumentValues();
26         cav.addIndexedArgumentValue(METHOD_INDEX, methodDef);
27 
28         Object pointcut = parsePointcutProperty(adviceElement, parserContext);
29         if (pointcut instanceof BeanDefinition) {
30             cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcut);
31             beanDefinitions.add((BeanDefinition) pointcut);
32         }
33         else if (pointcut instanceof String) {
34             RuntimeBeanReference pointcutRef = new RuntimeBeanReference((String) pointcut);
35             cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcutRef);
36             beanReferences.add(pointcutRef);
37         }
38 
39         cav.addIndexedArgumentValue(ASPECT_INSTANCE_FACTORY_INDEX, aspectFactoryDef);
40 
41         return adviceDefinition;
42     }

创建了adviceDefinition后第9第10行分别定义了通知类的一些属性,aspectName,declarationOrder

12 16 20行是判断有没有定义returning throwing arg-names属性,如果配置了,那么要给通知类加上

第25 26行要给这个通知类定义构造参数,通知类中的需要以下构造参数,比如AspectJMethodBeforeAdvice类的构造参数

AspectJMethodBeforeAdvice(
Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif)

它需要一个method对象,切面表达式连接点,切面实例工厂

第25 26行给这个通知的BeanDefinition的构造加入了第一个参数的BeanDefinition==》前面已经创建好的包装MethodLocatingFactoryBean的methodDefinition。

第28行是去通知标签(如before标签)上去得pointcut或者是pointcut-ref,代码如下:

 1 private Object parsePointcutProperty(Element element, ParserContext parserContext) {
 2         if (element.hasAttribute(POINTCUT) && element.hasAttribute(POINTCUT_REF)) {
 3             parserContext.getReaderContext().error(
 4                     "Cannot define both \'pointcut\' and \'pointcut-ref\' on <advisor> tag.",
 5                     element, this.parseState.snapshot());
 6             return null;
 7         }
 8         else if (element.hasAttribute(POINTCUT)) {
 9             // Create a pointcut for the anonymous pc and register it.
10             String expression = element.getAttribute(POINTCUT);
11             AbstractBeanDefinition pointcutDefinition = createPointcutDefinition(expression);
12             pointcutDefinition.setSource(parserContext.extractSource(element));
13             return pointcutDefinition;
14         }
15         else if (element.hasAttribute(POINTCUT_REF)) {
16             String pointcutRef = element.getAttribute(POINTCUT_REF);
17             if (!StringUtils.hasText(pointcutRef)) {
18                 parserContext.getReaderContext().error(
19                         "\'pointcut-ref\' attribute contains empty value.", element, this.parseState.snapshot());
20                 return null;
21             }
22             return pointcutRef;
23         }
24         else {
25             parserContext.getReaderContext().error(
26                     "Must define one of \'pointcut\' or \'pointcut-ref\' on <advisor> tag.",
27                     element, this.parseState.snapshot());
28             return null;
29         }
30     }

第8行判断用户定义是不是pointcut属性,如果是那么就执行了以下代码

protected AbstractBeanDefinition createPointcutDefinition(String expression) {
     RootBeanDefinition beanDefinition = new RootBeanDefinition(AspectJExpressionPointcut.class);
     beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
     beanDefinition.setSynthetic(true);
     beanDefinition.getPropertyValues().add(EXPRESSION, expression);
     return beanDefinition;
}

创建了一个包装了AspectJExpressionPointcut类的BeanDefinition,并且预设声明周期为prototype,属性值EXPRESSION为用户在xml上定义的表达式

如果不是pointcut属性,是pointcut-ref属性,那么直接返回,我们又会到createAdviceDefinition方法继续往下

 1 Object pointcut = parsePointcutProperty(adviceElement, parserContext);
 2         if (pointcut instanceof BeanDefinition) {
 3             cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcut);
 4             beanDefinitions.add((BeanDefinition) pointcut);
 5         }
 6         else if (pointcut instanceof String) {
 7             RuntimeBeanReference pointcutRef = new RuntimeBeanReference((String) pointcut);
 8             cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcutRef);
 9             beanReferences.add(pointcutRef);
10         }
11 
12         cav.addIndexedArgumentValue(ASPECT_INSTANCE_FACTORY_INDEX, aspectFactoryDef);

如果用户定义在通知标签上的属性为pointcut,那么会走第二行内的代码,如果不是,就会走第2行的代码,如果走第6行代码,那么就直接加入通知类的第二个构造参数

如果是走第6行的代码,那么就对pointcutref封装成BeanReference。成为第二个构造参数,BeanReference有个键beanName的属性,用来表示它引用了那个bean,到时候要使用的时候就是BeanFactory中拿。

第12行定义了通知类的第三个构造参数,这个构造参数是前面定义的,它是一个持有SimpleBeanFactoryAwareAspectInstanceFactory类的BeanDefinition。

此时一个持有通知类的BeanDefinition就准备好了,返回到parseAdvice中

 

 1 // register the pointcut
 2             AbstractBeanDefinition adviceDef = createAdviceDefinition(
 3                     adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef,
 4                     beanDefinitions, beanReferences);
 5 
 6             // configure the advisor
 7             RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class);
 8             advisorDefinition.setSource(parserContext.extractSource(adviceElement));
 9             advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef);
10             if (aspectElement.hasAttribute(ORDER_PROPERTY)) {
11                 advisorDefinition.getPropertyValues().add(
12                         ORDER_PROPERTY, aspectElement.getAttribute(ORDER_PROPERTY));
13             }
14 
15             // register the final advisor
16             parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);

红色标识部分是我们返回会来的方法,继续往下看第7行,这里有创建了一个持有AspectJPointcutAdvisor类的BeanDefinition,这个AspectJPointcutAdvisor有这么一些属性

advice, pointcut,advice是一个AbstractAspectJAdvice抽象通知类型的属性,

可以看到before,after等这些通知就是继承自它。

pointcut属性是一个Pointcut类型的属性,这里肯定是用来存AspectJExpressionPointcut子类。

 AspectJPointcutAdvisor类的构造器为AspectJPointcutAdvisor(AbstractAspectJAdvice advice)

 所以第九行果断给构造器定义了一个刚创建好的adviceDef参数

接着就执行了下面这段代码

1 public String registerWithGeneratedName(BeanDefinition beanDefinition) {
2         String generatedName = generateBeanName(beanDefinition);
3         getRegistry().registerBeanDefinition(generatedName, beanDefinition);
4         return generatedName;
5     }

传入的参数就是包装了AspectJPointcutAdvisor类的BeanDefinition,根据这个BeanDefinition生成名字形如org.springframework.aop.aspectj.AspectJPointcutAdvisor#1这样的名字,前面的类名,后面的数字表示这是创建的第几个AspectJPointcutAdvisor。这个无关紧要。

第三行将包装了AspectJPointcutAdvisor类的BeanDefinition添加到DefaultListableBeanFactory(BeanFactory子类)的beanDefinitionMap容器中

一切就绪后我们的方法返回到parseAspect方法

 1 AbstractBeanDefinition advisorDefinition = parseAdvice(
 2                             aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
 3                     beanDefinitions.add(advisorDefinition);
 4                 }
 5             }
 6 
 7             AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(
 8                     aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);
 9             parserContext.pushContainingComponent(aspectComponentDefinition);
10 
11             List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
12             for (Element pointcutElement : pointcuts) {
13                 parsePointcut(pointcutElement, parserContext);
14             }
15 
16             parserContext.popAndRegisterContainingComponent();

我们是从红色部分返回回来的。我们继续往下看第7行,它创建了一个AspectComponentDefinition对象,这个AspectComponentDefinition抛开父类,它有两个属性

private final BeanDefinition[] beanDefinitions;

private final BeanReference[] beanReferences;

它通过以下方法创建

private AspectComponentDefinition createAspectComponentDefinition(
            Element aspectElement, String aspectId, List<BeanDefinition> beanDefs,
            List<BeanReference> beanRefs, ParserContext parserContext) {

        BeanDefinition[] beanDefArray = beanDefs.toArray(new BeanDefinition[beanDefs.s

以上是关于spring aop的主要内容,如果未能解决你的问题,请参考以下文章

JAVA之AOP

Spring AOP

Spring源码高级笔记之——Spring AOP应用

2018.12.24 Spring中的aop演示

Spring框架 AOP

Spring的AOP面向切面编程