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的主要内容,如果未能解决你的问题,请参考以下文章