前言
前面写了六篇文章详细地分析了SpringBean加载流程,这部分完了之后就要进入一个比较困难的部分了,就是AOP的实现原理分析。为了探究AOP实现原理,首先定义几个类,一个Dao接口:
1publicinterfaceDao{ 2 3publicvoidselect(); 4 5publicvoidinsert(); 6 7}Dao接口的实现类DaoImpl:
1publicclassDaoImplimplementsDao{ 2 3@Override 4publicvoidselect(){ 5System.out.println("EnterDaoImpl.select()"); 6} 7 8@Override 9publicvoidinsert(){ 10System.out.println("EnterDaoImpl.insert()"); 11} 12 13}定义一个TimeHandler,用于方法调用前后打印时间,在AOP中,这扮演的是横切关注点的角色:
1publicclassTimeHandler{ 2 3publicvoidprintTime(){ 4System.out.println("CurrentTime:"+System.currentTimeMillis()); 5} 6 7}定义一个XML文件aop.xml:
1<?xmlversion="1.0"encoding="UTF-8"?> 2<beansxmlns="http://www.springframework.org/schema/beans" 3xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4xmlns:aop="http://www.springframework.org/schema/aop" 5xmlns:tx="http://www.springframework.org/schema/tx" 6xsi:schemaLocation="http://www.springframework.org/schema/beans 7http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 8http://www.springframework.org/schema/aop 9http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> 10 11<beanid="daoImpl"class="org.xrq.action.aop.DaoImpl"/> 12<beanid="timeHandler"class="org.xrq.action.aop.TimeHandler"/> 13 14<aop:configproxy-target-class="true"> 15<aop:aspectid="time"ref="timeHandler"> 16<aop:pointcutid="addAllMethod"expression="execution(*org.xrq.action.aop.Dao.*(..))"/> 17<aop:beforemethod="printTime"pointcut-ref="addAllMethod"/> 18<aop:aftermethod="printTime"pointcut-ref="addAllMethod"/> 19</aop:aspect> 20</aop:config> 21 22</beans>写一段测试代码TestAop.java:
1publicclassTestAop{ 2 3@Test 4publicvoidtestAop(){ 5ApplicationContextac=newClassPathXmlApplicationContext("spring/aop.xml"); 6 7Daodao=(Dao)ac.getBean("daoImpl"); 8dao.select(); 9} 10 11}代码运行结果就不看了,有了以上的内容,我们就可以根据这些跟一下代码,看看Spring到底是如何实现AOP的。
AOP实现原理----找到Spring处理AOP的源头
有很多朋友不愿意去看AOP源码的一个很大原因是因为找不到AOP源码实现的入口在哪里,这个确实是。不过我们可以看一下上面的测试代码,就普通Bean也好、AOP也好,最终都是通过getBean方法获取到Bean并调用方法的,getBean之后的对象已经前后都打印了TimeHandler类printTime()方法里面的内容,可以想见它们已经是被Spring容器处理过了。
既然如此,那无非就两个地方处理:
加载Bean定义的时候应该有过特殊的处理 getBean的时候应该有过特殊的处理因此,本文围绕【1.加载Bean定义的时候应该有过特殊的处理】展开,先找一下到底是哪里Spring对AOP做了特殊的处理。代码直接定位到DefaultBeanDefinitionDocumentReader的parseBeanDefinitions方法:
1protectedvoidparseBeanDefinitions(Elementroot,BeanDefinitionParserDelegatedelegate){ 2if(delegate.isDefaultNamespace(root)){ 3NodeListnl=root.getChildNodes(); 4for(inti=0;i<nl.getLength();i++){ 5Nodenode=nl.item(i); 6if(nodeinstanceofElement){ 7Elementele=(Element)node; 8if(delegate.isDefaultNamespace(ele)){ 9parseDefaultElement(ele,delegate); 10} 11else{ 12delegate.parseCustomElement(ele); 13} 14} 15} 16} 17else{ 18delegate.parseCustomElement(root); 19} 20}正常来说,遇到<beanid="daoImpl"...>、<beanid="timeHandler"...>这两个标签的时候,都会执行第9行的代码,因为标签是默认的Namespace。但是在遇到后面的aop:config标签的时候就不一样了,aop:config并不是默认的Namespace,因此会执行第12行的代码,看一下:
1publicBeanDefinitionparseCustomElement(Elementele,BeanDefinitioncontainingBd){ 2StringnamespaceUri=getNamespaceURI(ele); 3NamespaceHandlerhandler=this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); 4if(handler==null){ 5error("UnabletolocateSpringNamespaceHandlerforXMLschemanamespace["+namespaceUri+"]",ele); 6returnnull; 7} 8returnhandler.parse(ele,newParserContext(this.readerContext,this,containingBd)); 9}因为之前把整个XML解析为了org.w3c.dom.Document,org.w3c.dom.Document以树的形式表示整个XML,具体到每一个节点就是一个Node。
首先第2行从aop:config这个Node(参数Element是Node接口的子接口)中拿到Namespace="http://www.springframework.org/schema/aop",第3行的代码根据这个Namespace获取对应的NamespaceHandler即Namespace处理器,具体到aop这个Namespace的NamespaceHandler是org.springframework.aop.config.AopNamespaceHandler类,也就是第3行代码获取到的结果。具体到AopNamespaceHandler里面,有几个Parser,是用于具体标签转换的,分别为:
config-->ConfigBeanDefinitionParser aspectj-autoproxy-->AspectJAutoProxyBeanDefinitionParser scoped-proxy-->ScopedProxyBeanDefinitionDecorator spring-configured-->SpringConfiguredBeanDefinitionParser接着,就是第8行的代码,利用AopNamespaceHandler的parse方法,解析aop:config下的内容了。
AOPBean定义加载----根据织入方式将aop:before、aop:after转换成名为adviceDef的RootBeanDefinition
上面经过分析,已经找到了Spring是通过AopNamespaceHandler处理的AOP,那么接着进入AopNamespaceHandler的parse方法源代码:
1publicBeanDefinitionparse(Elementelement,ParserContextparserContext){ 2returnfindParserForElement(element,parserContext).parse(element,parserContext); 3}首先获取具体的Parser,因为当前节点是aop:config,上一部分最后有列,config是通过ConfigBeanDefinitionParser来处理的,因此findParserForElement(element,parserContext)这一部分代码获取到的是ConfigBeanDefinitionParser,接着看ConfigBeanDefinitionParser的parse方法:
1publicBeanDefinitionparse(Elementelement,ParserContextparserContext){ 2CompositeComponentDefinitioncompositeDef= 3newCompositeComponentDefinition(element.getTagName(),parserContext.extractSource(element)); 4parserContext.pushContainingComponent(compositeDef); 5 6configureAutoProxyCreator(parserContext,element); 7 8List<Element>childElts=DomUtils.getChildElements(element); 9for(Elementelt:childElts){ 10StringlocalName=parserContext.getDelegate().getLocalName(elt); 11if(POINTCUT.equals(localName)){ 12parsePointcut(elt,parserContext); 13} 14elseif(ADVISOR.equals(localName)){ 15parseAdvisor(elt,parserContext); 16} 17elseif(ASPECT.equals(localName)){ 18parseAspect(elt,parserContext); 19} 20} 21 22parserContext.popAndRegisterContainingComponent(); 23returnnull; 24}重点先提一下第6行的代码,该行代码的具体实现不跟了但它非常重要,configureAutoProxyCreator方法的作用我用几句话说一下:
向Spring容器注册了一个BeanName为org.springframework.aop.config.internalAutoProxyCreator的Bean定义,可以自定义也可以使用Spring提供的(根据优先级来) Spring默认提供的是org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator,这个类是AOP的核心类,留在下篇讲解 在这个方法里面也会根据配置proxy-target-class和expose-proxy,设置是否使用CGLIB进行代理以及是否暴露最终的代理。aop:config下的节点为aop:aspect,想见必然是执行第18行的代码parseAspect,跟进去:
1privatevoidparseAspect(ElementaspectElement,ParserContextparserContext){ 2StringaspectId=aspectElement.getAttribute(ID); 3StringaspectName=aspectElement.getAttribute(REF); 4 5try{ 6this.parseState.push(newAspectEntry(aspectId,aspectName)); 7List<BeanDefinition>beanDefinitions=newArrayList<BeanDefinition>(); 8List<BeanReference>beanReferences=newArrayList<BeanReference>(); 9 10List<Element>declareParents=DomUtils.getChildElementsByTagName(aspectElement,DECLARE_PARENTS); 11for(inti=METHOD_INDEX;i<declareParents.size();i++){ 12ElementdeclareParentsElement=declareParents.get(i); 13beanDefinitions.add(parseDeclareParents(declareParentsElement,parserContext)); 14} 15 16//Wehavetoparse"advice"andalltheadvicekindsinoneloop,togetthe 17//orderingsemanticsright. 18NodeListnodeList=aspectElement.getChildNodes(); 19booleanadviceFoundAlready=false; 20for(inti=0;i<nodeList.getLength();i++){ 21Nodenode=nodeList.item(i); 22if(isAdviceNode(node,parserContext)){ 23if(!adviceFoundAlready){ 24adviceFoundAlready=true; 25if(!StringUtils.hasText(aspectName)){ 26parserContext.getReaderContext().error( 27"<aspect>tagneedsaspectbeanreferenceviarefattributewhendeclaringadvices.", 28aspectElement,this.parseState.snapshot()); 29return; 30} 31beanReferences.add(newRuntimeBeanReference(aspectName)); 32} 33AbstractBeanDefinitionadvisorDefinition=parseAdvice( 34aspectName,i,aspectElement,(Element)node,parserContext,beanDefinitions,beanReferences); 35beanDefinitions.add(advisorDefinition); 36} 37} 38 39AspectComponentDefinitionaspectComponentDefinition=createAspectComponentDefinition( 40aspectElement,aspectId,beanDefinitions,beanReferences,parserContext); 41parserContext.pushContainingComponent(aspectComponentDefinition); 42 43List<Element>pointcuts=DomUtils.getChildElementsByTagName(aspectElement,POINTCUT); 44for(ElementpointcutElement:pointcuts){ 45parsePointcut(pointcutElement,parserContext); 46} 47 48parserContext.popAndRegisterContainingComponent(); 49} 50finally{ 51this.parseState.pop(); 52} 53}从第20行~第37行的循环开始关注这个方法。这个for循环有一个关键的判断就是第22行的ifAdviceNode判断,看下ifAdviceNode方法做了什么:
1privatebooleanisAdviceNode(NodeaNode,ParserContextparserContext){ 2if(!(aNodeinstanceofElement)){ 3returnfalse; 4} 5else{ 6Stringname=parserContext.getDelegate().getLocalName(aNode); 7return(BEFORE.equals(name)||AFTER.equals(name)||AFTER_RETURNING_ELEMENT.equals(name)|| 8AFTER_THROWING_ELEMENT.equals(name)||AROUND.equals(name)); 9} 10}即这个for循环只用来处理aop:aspect标签下的aop:before、aop:after、aop:after-returning、<aop:after-throwingmethod="">、<aop:aroundmethod="">这五个标签的。
接着,如果是上述五种标签之一,那么进入第33行~第34行的parseAdvice方法:
1privateAbstractBeanDefinitionparseAdvice( 2StringaspectName,intorder,ElementaspectElement,ElementadviceElement,ParserContextparserContext, 3List<BeanDefinition>beanDefinitions,List<BeanReference>beanReferences){ 4 5try{ 6this.parseState.push(newAdviceEntry(parserContext.getDelegate().getLocalName(adviceElement))); 7 8//createthemethodfactorybean 9RootBeanDefinitionmethodDefinition=newRootBeanDefinition(MethodLocatingFactoryBean.class); 10methodDefinition.getPropertyValues().add("targetBeanName",aspectName); 11methodDefinition.getPropertyValues().add("methodName",adviceElement.getAttribute("method")); 12methodDefinition.setSynthetic(true); 13 14//createinstancefactorydefinition 15RootBeanDefinitionaspectFactoryDef= 16newRootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class); 17aspectFactoryDef.getPropertyValues().add("aspectBeanName",aspectName); 18aspectFactoryDef.setSynthetic(true); 19 20//registerthepointcut 21AbstractBeanDefinitionadviceDef=createAdviceDefinition( 22adviceElement,parserContext,aspectName,order,methodDefinition,aspectFactoryDef, 23beanDefinitions,beanReferences); 24 25//configuretheadvisor 26RootBeanDefinitionadvisorDefinition=newRootBeanDefinition(AspectJPointcutAdvisor.class); 27advisorDefinition.setSource(parserContext.extractSource(adviceElement)); 28advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef); 29if(aspectElement.hasAttribute(ORDER_PROPERTY)){ 30advisorDefinition.getPropertyValues().add( 31ORDER_PROPERTY,aspectElement.getAttribute(ORDER_PROPERTY)); 32} 33 34//registerthefinaladvisor 35parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition); 36 37returnadvisorDefinition; 38} 39finally{ 40this.parseState.pop(); 41} 42}方法主要做了三件事:
根据织入方式(before、after这些)创建RootBeanDefinition,名为adviceDef即advice定义 将上一步创建的RootBeanDefinition写入一个新的RootBeanDefinition,构造一个新的对象,名为advisorDefinition,即advisor定义 将advisorDefinition注册到DefaultListableBeanFactory中下面来看做的第一件事createAdviceDefinition方法定义:
1privateAbstractBeanDefinitioncreateAdviceDefinition( 2ElementadviceElement,ParserContextparserContext,StringaspectName,intorder, 3RootBeanDefinitionmethodDef,RootBeanDefinitionaspectFactoryDef, 4List<BeanDefinition>beanDefinitions,List<BeanReference>beanReferences){ 5 6RootBeanDefinitionadviceDefinition=newRootBeanDefinition(getAdviceClass(adviceElement,parserContext)); 7adviceDefinition.setSource(parserContext.extractSource(adviceElement)); 8adviceDefinition.getPropertyValues().add(ASPECT_NAME_PROPERTY,aspectName); 9adviceDefinition.getPropertyValues().add(DECLARATION_ORDER_PROPERTY,order); 10 11if(adviceElement.hasAttribute(RETURNING)){ 12adviceDefinition.getPropertyValues().add( 13RETURNING_PROPERTY,adviceElement.getAttribute(RETURNING)); 14} 15if(adviceElement.hasAttribute(THROWING)){ 16adviceDefinition.getPropertyValues().add( 17THROWING_PROPERTY,adviceElement.getAttribute(THROWING)); 18} 19if(adviceElement.hasAttribute(ARG_NAMES)){ 20adviceDefinition.getPropertyValues().add( 21ARG_NAMES_PROPERTY,adviceElement.getAttribute(ARG_NAMES)); 22} 23 24ConstructorArgumentValuescav=adviceDefinition.getConstructorArgumentValues(); 25cav.addIndexedArgumentValue(METHOD_INDEX,methodDef); 26 27Objectpointcut=parsePointcutProperty(adviceElement,parserContext); 28if(pointcutinstanceofBeanDefinition){ 29cav.addIndexedArgumentValue(POINTCUT_INDEX,pointcut); 30beanDefinitions.add((BeanDefinition)pointcut); 31} 32elseif(pointcutinstanceofString){ 33RuntimeBeanReferencepointcutRef=newRuntimeBeanReference((String)pointcut); 34cav.addIndexedArgumentValue(POINTCUT_INDEX,pointcutRef); 35beanReferences.add(pointcutRef); 36} 37 38cav.addIndexedArgumentValue(ASPECT_INSTANCE_FACTORY_INDEX,aspectFactoryDef); 39 40returnadviceDefinition; 41}首先可以看到,创建的AbstractBeanDefinition实例是RootBeanDefinition,这和普通Bean创建的实例为GenericBeanDefinition不同。然后进入第6行的getAdviceClass方法看一下:
1privateClassgetAdviceClass(ElementadviceElement,ParserContextparserContext){ 2StringelementName=parserContext.getDelegate().getLocalName(adviceElement); 3if(BEFORE.equals(elementName)){ 4returnAspectJMethodBeforeAdvice.class; 5} 6elseif(AFTER.equals(elementName)){ 7returnAspectJAfterAdvice.class; 8} 9elseif(AFTER_RETURNING_ELEMENT.equals(elementName)){ 10returnAspectJAfterReturningAdvice.class; 11} 12elseif(AFTER_THROWING_ELEMENT.equals(elementName)){ 13returnAspectJAfterThrowingAdvice.class; 14} 15elseif(AROUND.equals(elementName)){ 16returnAspectJAroundAdvice.class; 17} 18else{ 19thrownewIllegalArgumentException("Unknownadvicekind["+elementName+"]."); 20} 21}既然创建Bean定义,必然该Bean定义中要对应一个具体的Class,不同的切入方式对应不同的Class:
before对应AspectJMethodBeforeAdvice After对应AspectJAfterAdvice after-returning对应AspectJAfterReturningAdvice after-throwing对应AspectJAfterThrowingAdvice around对应AspectJAroundAdvicecreateAdviceDefinition方法剩余逻辑没什么,就是判断一下标签里面的属性并设置一下相应的值而已,至此aop:before、aop:after两个标签对应的AbstractBeanDefinition就创建出来了。
AOPBean定义加载----将名为adviceDef的RootBeanDefinition转换成名为advisorDefinition的RootBeanDefinition
下面我们看一下第二步的操作,将名为adviceDef的RootBeanD转换成名为advisorDefinition的RootBeanDefinition,跟一下上面一部分ConfigBeanDefinitionParser类parseAdvice方法的第26行~32行的代码:
1RootBeanDefinitionadvisorDefinition=newRootBeanDefinition(AspectJPointcutAdvisor.class); 2advisorDefinition.setSource(parserContext.extractSource(adviceElement)); 3advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef); 4if(aspectElement.hasAttribute(ORDER_PROPERTY)){ 5advisorDefinition.getPropertyValues().add( 6ORDER_PROPERTY,aspectElement.getAttribute(ORDER_PROPERTY)); 7}这里相当于将上一步生成的RootBeanDefinition包装了一下,new一个新的RootBeanDefinition出来,Class类型是org.springframework.aop.aspectj.AspectJPointcutAdvisor。
第4行~第7行的代码是用于判断aop:aspect标签中有没有"order"属性的,有就设置一下,"order"属性是用来控制切入方法优先级的。
AOPBean定义加载----将BeanDefinition注册到DefaultListableBeanFactory中
最后一步就是将BeanDefinition注册到DefaultListableBeanFactory中了,代码就是前面ConfigBeanDefinitionParser的parseAdvice方法的最后一部分了:
1... 2//registerthefinaladvisor 3parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition); 4...跟一下registerWithGeneratedName方法的实现:
1publicStringregisterWithGeneratedName(BeanDefinitionbeanDefinition){ 2StringgeneratedName=generateBeanName(beanDefinition); 3getRegistry().registerBeanDefinition(generatedName,beanDefinition); 4returngeneratedName; 5}第2行获取注册的名字BeanName,和的注册差不多,使用的是Class全路径+"#"+全局计数器的方式,其中的Class全路径为org.springframework.aop.aspectj.AspectJPointcutAdvisor,依次类推,每一个BeanName应当为org.springframework.aop.aspectj.AspectJPointcutAdvisor#0、org.springframework.aop.aspectj.AspectJPointcutAdvisor#1、org.springframework.aop.aspectj.AspectJPointcutAdvisor#2这样下去。
第3行向DefaultListableBeanFactory中注册,BeanName已经有了,剩下的就是Bean定义,Bean定义的解析流程之前已经看过了,就不说了。
AOPBean定义加载----AopNamespaceHandler处理aop:pointcut流程
回到ConfigBeanDefinitionParser的parseAspect方法:
1privatevoidparseAspect(ElementaspectElement,ParserContextparserContext){ 2 3... 4 5AspectComponentDefinitionaspectComponentDefinition=createAspectComponentDefinition( 6aspectElement,aspectId,beanDefinitions,beanReferences,parserContext); 7parserContext.pushContainingComponent(aspectComponentDefinition); 8 9List<Element>pointcuts=DomUtils.getChildElementsByTagName(aspectElement,POINTCUT); 10for(ElementpointcutElement:pointcuts){ 11parsePointcut(pointcutElement,parserContext); 12} 13 14parserContext.popAndRegisterContainingComponent(); 15} 16finally{ 17this.parseState.pop(); 18} 19}省略号部分表示是解析的是aop:before、aop:after这种标签,上部分已经说过了,就不说了,下面看一下解析aop:pointcut部分的源码。
第5行~第7行的代码构建了一个Aspect标签组件定义,并将Apsect标签组件定义推到ParseContext即解析工具上下文中,这部分代码不是关键。
第9行的代码拿到所有aop:aspect下的pointcut标签,进行遍历,由parsePointcut方法进行处理:
1privateAbstractBeanDefinitionparsePointcut(ElementpointcutElement,ParserContextparserContext){ 2Stringid=pointcutElement.getAttribute(ID); 3Stringexpression=pointcutElement.getAttribute(EXPRESSION); 4 5AbstractBeanDefinitionpointcutDefinition=null; 6 7try{ 8this.parseState.push(newPointcutEntry(id)); 9pointcutDefinition=createPointcutDefinition(expression); 10pointcutDefinition.setSource(parserContext.extractSource(pointcutElement)); 11 12StringpointcutBeanName=id; 13if(StringUtils.hasText(pointcutBeanName)){ 14parserContext.getRegistry().registerBeanDefinition(pointcutBeanName,pointcutDefinition); 15} 16else{ 17pointcutBeanName=parserContext.getReaderContext().registerWithGeneratedName(pointcutDefinition); 18} 19 20parserContext.registerComponent( 21newPointcutComponentDefinition(pointcutBeanName,pointcutDefinition,expression)); 22} 23finally{ 24this.parseState.pop(); 25} 26 27returnpointcutDefinition; 28}第2行~第3行的代码获取aop:pointcut标签下的"id"属性与"expression"属性。
第8行的代码推送一个PointcutEntry,表示当前Spring上下文正在解析Pointcut标签。
第9行的代码创建Pointcut的Bean定义,之后再看,先把其他方法都看一下。
第10行的代码不管它,最终从NullSourceExtractor的extractSource方法获取Source,就是个null。
第12行~第18行的代码用于注册获取到的Bean定义,默认pointcutBeanName为aop:pointcut标签中定义的id属性:
如果aop:pointcut标签中配置了id属性就执行的是第13行~第15行的代码,pointcutBeanName=id 如果aop:pointcut标签中没有配置id属性就执行的是第16行~第18行的代码,和Bean不配置id属性一样的规则,pointcutBeanName=org.springframework.aop.aspectj.AspectJExpressionPointcut#序号(从0开始累加)第20行~第21行的代码向解析工具上下文中注册一个Pointcut组件定义
第23行~第25行的代码,finally块在aop:pointcut标签解析完毕后,让之前推送至栈顶的PointcutEntry出栈,表示此次aop:pointcut标签解析完毕。
最后回头来一下第9行代码createPointcutDefinition的实现,比较简单:
1protectedAbstractBeanDefinitioncreatePointcutDefinition(Stringexpression){ 2RootBeanDefinitionbeanDefinition=newRootBeanDefinition(AspectJExpressionPointcut.class); 3beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE); 4beanDefinition.setSynthetic(true); 5beanDefinition.getPropertyValues().add(EXPRESSION,expression); 6returnbeanDefinition; 7}关键就是注意一下两点:
aop:pointcut标签对应解析出来的BeanDefinition是RootBeanDefinition,且RootBenaDefinitoin中的Class是org.springframework.aop.aspectj.AspectJExpressionPointcut aop:pointcut标签对应的Bean是prototype即原型的这样一个流程下来,就解析了aop:pointcut标签中的内容并将之转换为RootBeanDefintion存储在Spring容器中。
本文内容总结:
原文链接:https://www.cnblogs.com/xrq730/p/6753160.html