相關背景及資源:html
曹工說Spring Boot源碼(1)-- Bean Definition究竟是什麼,附spring思惟導圖分享java
曹工說Spring Boot源碼(2)-- Bean Definition究竟是什麼,我們對着接口,逐個方法講解node
曹工說Spring Boot源碼(3)-- 手動註冊Bean Definition不比遊戲好玩嗎,咱們來試一下git
曹工說Spring Boot源碼(4)-- 我是怎麼自定義ApplicationContext,從json文件讀取bean definition的?spring
曹工說Spring Boot源碼(5)-- 怎麼從properties文件讀取beanexpress
曹工說Spring Boot源碼(6)-- Spring怎麼從xml文件裏解析bean的json
曹工說Spring Boot源碼(7)-- Spring解析xml文件,到底從中獲得了什麼(上)框架
曹工說Spring Boot源碼(8)-- Spring解析xml文件,到底從中獲得了什麼(util命名空間)ide
曹工說Spring Boot源碼(9)-- Spring解析xml文件,到底從中獲得了什麼(context命名空間上)函數
曹工說Spring Boot源碼(10)-- Spring解析xml文件,到底從中獲得了什麼(context:annotation-config 解析)
曹工說Spring Boot源碼(11)-- context:component-scan,你真的會用嗎(此次來講說它的奇技淫巧)
曹工說Spring Boot源碼(12)-- Spring解析xml文件,到底從中獲得了什麼(context:component-scan完整解析)
曹工說Spring Boot源碼(13)-- AspectJ的運行時織入(Load-Time-Weaving),基本內容是講清楚了(附源碼)
曹工說Spring Boot源碼(14)-- AspectJ的Load-Time-Weaving的兩種實現方式細細講解,以及怎麼和Spring Instrumentation集成
曹工說Spring Boot源碼(15)-- Spring從xml文件裏到底獲得了什麼(context:load-time-weaver 完整解析)
曹工說Spring Boot源碼(16)-- Spring從xml文件裏到底獲得了什麼(aop:config完整解析【上】)
工程結構圖:
本篇是接着上一篇講的,爲了不沒必要要的重複,請你們先看下前一篇。
曹工說Spring Boot源碼(16)-- Spring從xml文件裏到底獲得了什麼(aop:config完整解析【上】)
本篇主要講一個主題,解析xml後,獲取到了哪些bean definition。
爲了講述方便,這裏貼一下spring要解析的xml:
<!--目標對象--> <bean id="performer" class="foo.Performer"/> <!--切面--> <bean id="performAspect" class="foo.PerformAspect"/> <!--配置切入點--> <aop:config> <aop:pointcut id="mypointcut" expression="execution(public * foo.Perform.sing(..))"/> <aop:aspect id="myAspect" ref="performAspect"> <aop:after method="afterPerform" pointcut-ref="mypointcut"/> </aop:aspect> </aop:config>
前面兩個業務bean,沒啥說的,一個是target對象,一個是切面對象。核心的解析主要是
public class AopNamespaceHandler extends NamespaceHandlerSupport { public void init() { // In 2.0 XSD as well as in 2.1 XSD. registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser()); registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser()); registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator()); // Only in 2.0 XSD: moved to context namespace as of 2.1 這個已經移到context命名空間了 registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser()); } }
aop命名空間裏,一共4個元素,其中一個"spring-configured"移到了context命名空間了,因此剩三個。這三個元素,對應的解析類,在上面的init方法中一目瞭然。其中,aop:config對應的解析類,爲ConfigBeanDefinitionParser。
在下面的parse方法中,參數element即爲當前解析到的
public BeanDefinition parse(Element element, ParserContext parserContext) { // 配置代理建立bean definition,是一個beanPostProcessor類型的bean definition configureAutoProxyCreator(parserContext, element); // 獲取aop元素下的子元素 List<Element> childElts = DomUtils.getChildElements(element); for (Element elt: childElts) { String localName = parserContext.getDelegate().getLocalName(elt); // 若是元素名等於pointcut,則走下面 if (POINTCUT.equals(localName)) { parsePointcut(elt, parserContext); } // 若是元素名等於 advisor,則走下面 else if (ADVISOR.equals(localName)) { parseAdvisor(elt, parserContext); } // 若是元素名等於 aspect,則走下面 else if (ASPECT.equals(localName)) { parseAspect(elt, parserContext); } } return null; }
爲了講解清晰,咱們先講解幾個子元素的解析過程。
#org.springframework.aop.config.ConfigBeanDefinitionParser#parsePointcut private AbstractBeanDefinition parsePointcut(Element pointcutElement, ParserContext parserContext) { //獲取id和expression屬性 String id = pointcutElement.getAttribute(ID); String expression = pointcutElement.getAttribute(EXPRESSION); // 1. 根據expression,建立bean definition AbstractBeanDefinition pointcutDefinition = createPointcutDefinition(expression);; // 2. 向ioc容器,註冊bean definition,註冊的操做是由下面的registerBeanDefinition調用完成 String pointcutBeanName = id; if (StringUtils.hasText(pointcutBeanName)) { parserContext.getRegistry().registerBeanDefinition(pointcutBeanName, pointcutDefinition); } else { pointcutBeanName = parserContext.getReaderContext().registerWithGeneratedName(pointcutDefinition); } return pointcutDefinition; }
上面的代碼,主要有2個步驟,生成bean definition和向BeanDefinitionRegistry(通常beanFactory實現了該接口)註冊該bean definition。
生成beanDefinition的代碼,主要在如下方法:
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; }
你們能夠看到,這裏new了一個RootBeanDefinition,這是一個BeanDefinition接口的實現,框架內部的bean的beandefinition,通常都是這個類型。這裏能夠看到,這個bean definition的class類型爲AspectJExpressionPointcut,scope爲prototype,並且經過如下代碼,設置了一個屬性值。
beanDefinition.getPropertyValues().add(EXPRESSION, expression);
propertValues這個屬性,你們能夠理解爲xml時代,像下面這樣配置屬性:
<bean class="foo.TestPropertiesVO"> <property name="name" value="abc"/> </bean>
其實也不能說是「像」,由於spring解析上面這個xml,就會使用beanDefinition.getPropertyValues().add(EXPRESSION, expression)
這樣的代碼來解析。
ok,切點解析,咱們就是獲得了一個AspectJExpressionPointcut
類型的bean definition。
# org.springframework.aop.config.ConfigBeanDefinitionParser#parseAspect # 去掉了部分無關代碼 private void parseAspect(Element aspectElement, ParserContext parserContext) { String aspectId = aspectElement.getAttribute(ID); String aspectName = aspectElement.getAttribute(REF); List<BeanDefinition> beanDefinitions = new ArrayList<BeanDefinition>(); List<BeanReference> beanReferences = new ArrayList<BeanReference>(); 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; // 這裏實際上是要把<aop:aspect ref="performAspect"> 這一句裏面的ref引用的切面存起來 beanReferences.add(new RuntimeBeanReference(aspectName)); } // 解析每個子元素,獲取一個bean definition。這裏的子元素就是<aop:before> <aop:after>等 AbstractBeanDefinition advisorDefinition = parseAdvice( aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences); beanDefinitions.add(advisorDefinition); } } }
上面這段代碼,有3點要說明的。
這裏經過String aspectName = aspectElement.getAttribute(REF);
獲取了通知bean的bean name,也就是<aop:aspect ref="performAspect">
這裏面的那個performAspect。這個東西,後面會用;
aspectElement.getChildNodes();
獲取了子元素,當前是<aop:aspect ref="performAspect">
,那麼子元素就是<aop:before>
,<aop:after>
這些。每次遍歷,都會生成一個AbstractBeanDefinition advisorDefinition
,也就是說,每次遍歷都會生成一個bean definition。
具體的<aop:after>
代碼以下:
private AbstractBeanDefinition parseAdvice( String aspectName, int order, Element aspectElement, Element adviceElement, ParserContext parserContext, List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) { // 1.建立bean definition,類型爲 MethodLocatingFactoryBean;會交給第三步使用 RootBeanDefinition methodDefinition = new RootBeanDefinition(MethodLocatingFactoryBean.class); methodDefinition.getPropertyValues().add("targetBeanName", aspectName); methodDefinition.getPropertyValues().add("methodName", adviceElement.getAttribute("method")); methodDefinition.setSynthetic(true); // 2.建立bean definition,類型爲SimpleBeanFactoryAwareAspectInstanceFactory;會交給第三步使用 RootBeanDefinition aspectFactoryDef = new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class); aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName); aspectFactoryDef.setSynthetic(true); // 3.建立aop:after對應的類型的bean definition;若是是aop:before,這裏的類型不同 AbstractBeanDefinition adviceDef = createAdviceDefinition( adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef, beanDefinitions, beanReferences); // 4. 建立bean definition,類型爲 AspectJPointcutAdvisor RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class); advisorDefinition.setSource(parserContext.extractSource(adviceElement)); advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef); if (aspectElement.hasAttribute(ORDER_PROPERTY)) { advisorDefinition.getPropertyValues().add( ORDER_PROPERTY, aspectElement.getAttribute(ORDER_PROPERTY)); } // 5. 註冊第四步建立的AspectJPointcutAdvisor類型的bean definition parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition); return advisorDefinition; }
有人看到上面一坨,不要慌,其實不難。第一步和第二步,建立了2個bean definition,都是給第三步服務的。第三步,我這裏給你們說,根據不一樣的子元素,bean definition的class是不同的,你們直接看如下代碼:
org.springframework.aop.config.ConfigBeanDefinitionParser#getAdviceClass private Class getAdviceClass(Element adviceElement, ParserContext parserContext) { String elementName = parserContext.getDelegate().getLocalName(adviceElement); if (BEFORE.equals(elementName)) { return AspectJMethodBeforeAdvice.class; } else if (AFTER.equals(elementName)) { return AspectJAfterAdvice.class; } else if (AFTER_RETURNING_ELEMENT.equals(elementName)) { return AspectJAfterReturningAdvice.class; } else if (AFTER_THROWING_ELEMENT.equals(elementName)) { return AspectJAfterThrowingAdvice.class; } else if (AROUND.equals(elementName)) { return AspectJAroundAdvice.class; } else { throw new IllegalArgumentException("Unknown advice kind [" + elementName + "]."); } }
由於咱們這裏是aop:after
,因此咱們這裏的bean 類型爲AspectJAfterAdvice。
咱們進一步,看看AspectJAfterAdvice這個類的代碼:
public class AspectJAfterAdvice extends AbstractAspectJAdvice implements MethodInterceptor, AfterAdvice { public AspectJAfterAdvice( Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) { super(aspectJBeforeAdviceMethod, pointcut, aif); } ... }
能夠看到,這個class,只有一個構造函數,須要三個參數。咱們仔細看看,其實前面的第一步和第二步,建立的bean definition,就是給這個構造函數服務的。
AspectJAfterAdvice構造函數參數 | 解析代碼中建立的bean definition |
---|---|
Method aspectJBeforeAdviceMethod | 步驟1,類型爲MethodLocatingFactoryBean。其實現了接口FactoryBean<Method> ,經過ioc容器,獲取factorybean,直接就能獲取到其生產的對象,這裏這個工廠,生產的對象,就是Method類型的 |
AspectJExpressionPointcut pointcut | 步驟3,todo |
AspectInstanceFactory aif | 步驟2,類型爲SimpleBeanFactoryAwareAspectInstanceFactory,其實現了AspectInstanceFactory接口 |
你們看了這個表格,應該清楚了很多,其中第二個參數還沒講到,咱們跳轉到步驟3的具體實現中:
private AbstractBeanDefinition createAdviceDefinition( Element adviceElement, ParserContext parserContext, String aspectName, int order, RootBeanDefinition methodDef, RootBeanDefinition aspectFactoryDef, List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) { // 這裏的getAdviceClass,就是根據元素類型,獲取不一樣的bean class類型 RootBeanDefinition adviceDefinition = new RootBeanDefinition(getAdviceClass(adviceElement, parserContext)); // 設置aspectName屬性,來源於<aop:aspect ref="performAspect"> adviceDefinition.getPropertyValues().add(ASPECT_NAME_PROPERTY, aspectName); // 設置本 bean definition的類的構造參數,咱們這裏,即AspectJAfterAdvice的構造函數參數 ConstructorArgumentValues cav = adviceDefinition.getConstructorArgumentValues(); cav.addIndexedArgumentValue(0, methodDef); Object pointcut = parsePointcutProperty(adviceElement, parserContext); RuntimeBeanReference pointcutRef = new RuntimeBeanReference((String) pointcut); cav.addIndexedArgumentValue(1, pointcutRef); beanReferences.add(pointcutRef); cav.addIndexedArgumentValue(2, aspectFactoryDef); return adviceDefinition; }
我想了下,拿圖說話吧:
這個AspectJAfterAdvice的bean definition中的構造函數參數這塊,就接收上面圖裏的3個參數,其中參數1和3,都是RootBeanDefinition;參數2,爲針對bean的引用。
前面講了,怎麼去構造AspectJAfterAdvice這種bean definition了,但還有一段沒講:
// 4. 建立bean definition,類型爲 AspectJPointcutAdvisor RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class); advisorDefinition.setSource(parserContext.extractSource(adviceElement)); advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef); if (aspectElement.hasAttribute(ORDER_PROPERTY)) { advisorDefinition.getPropertyValues().add( ORDER_PROPERTY, aspectElement.getAttribute(ORDER_PROPERTY)); } // 5. 註冊第四步建立的AspectJPointcutAdvisor類型的bean definition parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition); return advisorDefinition; }
這裏其實就是利用前面拿到的AspectJAfterAdvice,去構造這裏第四步的AspectJPointcutAdvisor類型的bean definition。你們直接看看以下代碼:
public AspectJPointcutAdvisor(AbstractAspectJAdvice advice) { Assert.notNull(advice, "Advice must not be null"); this.advice = advice; this.pointcut = advice.buildSafePointcut(); }
因此,就是說,第四步的bean definition,在構造這個bean的時候,由於沒有無參構造函數,而只有這個帶一個AbstractAspectJAdvice類型參數的構造函數。
具體的,你們看下面的圖,更容易理解。
而後,這個最外層的AspectJPointcutAdvisor
的bean definition,被註冊到ioc容器;而值得一提的是,其餘的幾個bean definition,並無被註冊到ioc容器。
彙總一下,目前爲止,解析下面這段xml,咱們的收穫以下:
<aop:config> <aop:pointcut id="pointcut" expression="execution(public * foo.Perform.sing(..))"/> <aop:aspect ref="performAspect"> <aop:before method="beforePerform" pointcut-ref="pointcut"/> <aop:after method="afterPerform" pointcut-ref="pointcut"/> </aop:aspect> </aop:config>
收穫:
切點對應的bean definition,1個
{ "abstract": false, "autowireCandidate": true, "autowireMode": 0, "beanClass": "org.springframework.aop.aspectj.AspectJExpressionPointcut", "beanClassName": "org.springframework.aop.aspectj.AspectJExpressionPointcut", "constructorArgumentValues": { "argumentCount": 0, "empty": true, "genericArgumentValues": [], "indexedArgumentValues": {} }, "dependencyCheck": 0, "enforceDestroyMethod": true, "enforceInitMethod": true, "lazyInit": false, "primary": false, "propertyValues": { "converted": false, "empty": false, "propertyValueList": [ { "converted": false, "name": "expression", "optional": false, "value": "execution(public * foo.Perform.sing(..))" } ] }, "prototype": true, "qualifiers": [], "resolvedAutowireMode": 0, "role": 0, "scope": "prototype", "singleton": false, "synthetic": true, "targetType": "org.springframework.aop.aspectj.AspectJExpressionPointcut" }
其內部的構造函數參數,持有了一個內部的,類型爲AspectJAfterAdvice的bean definition,這個實際上是一個內部bean definition了。而這個內部bean definition的構造函數中,還持有了3個其餘的參數,2個bean definition,1個爲bean 引用。你們能夠看下面的json,比較長,我已經刪了一些無關屬性了。
{ "abstract": false, "autowireCandidate": true, "autowireMode": 0, "beanClass": "org.springframework.aop.aspectj.AspectJPointcutAdvisor", "beanClassName": "org.springframework.aop.aspectj.AspectJPointcutAdvisor", "constructorArgumentValues": { "argumentCount": 1, "empty": false, "genericArgumentValues": [ { "converted": false, "value": { "abstract": false, "autowireCandidate": true, "autowireMode": 0, // 看這裏 "beanClass": "org.springframework.aop.aspectj.AspectJAfterAdvice", "beanClassName": "org.springframework.aop.aspectj.AspectJAfterAdvice", "constructorArgumentValues": { "argumentCount": 3, "empty": false, "genericArgumentValues": [], // 再看這裏,3個構造函數參數 "indexedArgumentValues": { "0": { "converted": false, "value": { "abstract": false, "autowireCandidate": true, "autowireMode": 0, "beanClass": "org.springframework.aop.config.MethodLocatingFactoryBean", "beanClassName": "org.springframework.aop.config.MethodLocatingFactoryBean", "constructorArgumentValues": { "argumentCount": 0, "empty": true, "genericArgumentValues": [], "indexedArgumentValues": {} }, "nonPublicAccessAllowed": true, "primary": false, "propertyValues": { "converted": false, "empty": false, "propertyValueList": [ { "converted": false, "name": "targetBeanName", "optional": false, "value": "performAspect" }, { "converted": false, "name": "methodName", "optional": false, "value": "afterPerform" } ] }, "prototype": false, "qualifiers": [], "resolvedAutowireMode": 0, "role": 0, "scope": "", "singleton": true, "synthetic": true } }, "1": { "converted": false, "value": { "beanName": "mypointcut", "toParent": false } }, "2": { "converted": false, "value": { "abstract": false, "autowireCandidate": true, "autowireMode": 0, "beanClass": "org.springframework.aop.config.SimpleBeanFactoryAwareAspectInstanceFactory", "beanClassName": "org.springframework.aop.config.SimpleBeanFactoryAwareAspectInstanceFactory", "constructorArgumentValues": { "argumentCount": 0, "empty": true, "genericArgumentValues": [], "indexedArgumentValues": {} }, "dependencyCheck": 0, "enforceDestroyMethod": true, "enforceInitMethod": true, "lazyInit": false, "lenientConstructorResolution": true, "methodOverrides": { "empty": true, "overrides": [] }, "nonPublicAccessAllowed": true, "primary": false, "propertyValues": { "converted": false, "empty": false, "propertyValueList": [ { "converted": false, "name": "aspectBeanName", "optional": false, "value": "performAspect" } ] }, } } } }, "nonPublicAccessAllowed": true, "propertyValues": { "converted": false, "empty": false, "propertyValueList": [ { "converted": false, "name": "aspectName", "optional": false, "value": "performAspect" }, { "converted": false, "name": "declarationOrder", "optional": false, "value": 1 } ] }, "prototype": false, "singleton": true, "synthetic": false } } ], "indexedArgumentValues": {} }, "lazyInit": false, "primary": false, "propertyValues": { "converted": false, "empty": true, "propertyValueList": [] }, "targetType": "org.springframework.aop.aspectj.AspectJPointcutAdvisor" }
因此,到目前爲止,咱們收穫了2個最外層的,註冊到了ioc容器的bean definition,是能夠直接getBean的那種。
至於其他的那幾個構造函數相關的bean definition,其實都是在ioc容器裏不存在的,若是去getBean,會失敗。
好比,咱們改造了main方法以下:
public static void main(String[] args) { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext( "context-namespace-test-aop.xml"); // 這裏去獲取前面那個AspectJAfterAdvice bean definition AspectJAfterAdvice bean = ctx.getBean(AspectJAfterAdvice.class); Perform performer = (Perform) ctx.getBean(Perform.class); performer.sing(); }
結果,報錯了,NoSuchBeanDefinitionException:
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.aop.aspectj.AspectJAfterAdvice] is defined at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:296) at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1196) at foo.Main.main(Main.java:21)
換成下面這個,也同樣:
ctx.getBean(SimpleBeanFactoryAwareAspectInstanceFactory.class);
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.aop.config.SimpleBeanFactoryAwareAspectInstanceFactory] is defined at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:296) at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1196) at foo.Main.main(Main.java:22)
通過這麼一實驗,想必你們也能必定程度,理解內部bean了。
前面一段解析,雖然費時費力,可是還沒完成所有的解析工做,拿到的都是些業務bean definition,好比在什麼地方切,切面邏輯在哪,等等,可是,這個切面要怎麼生效,還沒搞清楚。你們能夠往前翻,翻到開頭的解析處,能夠看到下面這段:
public BeanDefinition parse(Element element, ParserContext parserContext) { CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element)); parserContext.pushContainingComponent(compositeDef); // 配置代理建立bean definition,是一個beanPostProcessor類型的bean definition configureAutoProxyCreator(parserContext, element); ... }
其中,configureAutoProxyCreator這句,就是畫龍點睛的最後一筆。其通過簡單的跳轉後,會調用:
public static BeanDefinition registerAspectJAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) { return registerOrEscalateApcAsRequired(AspectJAwareAdvisorAutoProxyCreator.class, registry, source); }
這一句,會註冊一個bean class類型爲AspectJAwareAdvisorAutoProxyCreator的bean definition,到ioc容器。
這個AspectJAwareAdvisorAutoProxyCreator類比較特別,
上圖可知,其實現了 BeanPostProcessor接口,能夠在bean的初始化先後進行一些處理,好比什麼處理呢?好比狸貓換太子,將真正的bean換成動態代理後的bean。
寫到這裏,感受內容已經有點過於長了,也不方便你們理解吸取。具體的,這個AspectJAwareAdvisorAutoProxyCreator,做爲BeanPostProcessor,如何去建立代理,咱們放到下一節好好說。
同時,也會看看,在獲取AspectJPointcutAdvisor這個bean的時候,有什麼特別之處。