相關背景及資源:html
曹工說Spring Boot源碼(1)-- Bean Definition究竟是什麼,附spring思惟導圖分享java
曹工說Spring Boot源碼(2)-- Bean Definition究竟是什麼,我們對着接口,逐個方法講解git
曹工說Spring Boot源碼(3)-- 手動註冊Bean Definition不比遊戲好玩嗎,咱們來試一下spring
曹工說Spring Boot源碼(4)-- 我是怎麼自定義ApplicationContext,從json文件讀取bean definition的?express
曹工說Spring Boot源碼(5)-- 怎麼從properties文件讀取beanjson
曹工說Spring Boot源碼(6)-- Spring怎麼從xml文件裏解析bean的數組
曹工說Spring Boot源碼(7)-- Spring解析xml文件,到底從中獲得了什麼(上)數據結構
曹工說Spring Boot源碼(8)-- Spring解析xml文件,到底從中獲得了什麼(util命名空間)app
曹工說Spring Boot源碼(9)-- Spring解析xml文件,到底從中獲得了什麼(context命名空間上)ide
曹工說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源碼(17)-- Spring從xml文件裏到底獲得了什麼(aop:config完整解析【中】)
工程結構圖:
本篇是接着前兩篇講的,爲了不沒必要要的重複,請你們先看下。
曹工說Spring Boot源碼(16)-- Spring從xml文件裏到底獲得了什麼(aop:config完整解析【上】)
曹工說Spring Boot源碼(17)-- Spring從xml文件裏到底獲得了什麼(aop:config完整解析【中】)
本篇主要講解spring如何在bean生成過程當中,完成「狸貓換太子」操做的。(利用代理對象,替換ioc容器中的原有bean)。
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 definition。
bean definition 中bean class | 備註 |
---|---|
PerformAspect | 通知 |
Performer | 要切的目標 |
AspectJExpressionPointcut | 切點,即
|
org.springframework.aop.aspectj.AspectJPointcutAdvisor | advisor,解析
|
AspectJAwareAdvisorAutoProxyCreator | 實現了BeanPostProcessor接口,解析<aop:config> 獲得 |
解析xml、註解,經過各類渠道,獲得bean definition;
從bean definition集合中,找出bean class實現了BeanFactoryPostProcessor接口的子集,而後經過getBean來獲取這部分特殊的bean,而後依次調用其postProcessBeanFactory方法
來對其他的bean definition進行處理;
public interface BeanFactoryPostProcessor { /** * Modify the application context's internal bean factory after its standard * initialization. All bean definitions will have been loaded, but no beans * will have been instantiated yet. This allows for overriding or adding * properties even to eager-initializing beans. * @param beanFactory the bean factory used by the application context * @throws org.springframework.beans.BeansException in case of errors */ void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory); }
從bean definition集合中,找出bean class實現了BeanPostProcessor接口的子集,而後經過getBean來獲取這部分特殊的bean,而後保存起來。
找出不具備特殊功能的(沒實現BeanFactoryPostProcessor,也沒實現BeanPostProcessor)bean definition 集合,再過濾出單例bean,且lazy-init屬性爲false(說明要在spring容器初始化過程當中,實例化的bean)的這部分,做爲子集;而後依次調用其getBean方法。
以上就是spring比較粗的流程,固然,不少細節沒說,不過核心流程是這樣的。
在前面的spring流程中,第三步,就會去查找實現了BeanPostProcessor的bean definition集合,其中,就會包含AspectJAwareAdvisorAutoProxyCreator 這個bean definition;而後經過getBean方法,將這個bean definition實例化爲bean。(這個過程,相似於經過class來建立對象)
這個步驟的具體實現入口在:
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); /** * 配置beanFactory */ prepareBeanFactory(beanFactory); try { postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // 入口在這裏!這裏面會去 getBean registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); /** * 這裏對單例bean、且lazy-init=false進行實例化 */ finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } } }
具體跟進去後,在以下位置,809行,就經過getBean,獲取到了AspectJAwareAdvisorAutoProxyCreator這個bean了:
在「spring大體啓動流程」中的第四步,去預先實例化bean時,會進入如下邏輯:
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { ... try { ... // Check for listener beans and register them. registerListeners(); /** * 入口在這裏。這裏對單例bean、且lazy-init=false進行實例化 */ finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } } }
而後會進入如下邏輯:
org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons public void preInstantiateSingletons() throws BeansException { if (this.logger.isInfoEnabled()) { this.logger.info("Pre-instantiating singletons in " + this); } // 其中 this.beanDefinitionNames 保存了全部的bean definition名稱 for (String beanName : this.beanDefinitionNames) { RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); // 實例化那些:非抽象、單例、非lazy-init的bean definition if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { if (isFactoryBean(beanName)) { ... if (isEagerInit) { getBean(beanName); } } else { getBean(beanName); } } } }
因此,這裏就是對那些:非抽象、單例、非lazy-init的bean definition 進行實例化。
咱們這裏,你們看下當前容器中包含的所有的bean definition:
我這邊的demo,第一個要實例化的bean,就是performer。在getBean()處理performer這個bean的過程當中,會經歷如下的流程:
其中,AspectJAwareAdvisorAutoProxyCreator 做爲beanPostProcessor,在實例化performer這個bean時,有兩個時間點參與進來。
哪兩個時間點呢,你們再看看,AspectJAwareAdvisorAutoProxyCreator 的類圖:
其實它不止實現了BeanPostProcessor
接口,它還實現了InstantiationAwareBeanPostProcessor
接口。這個多出來的接口,幹嗎的?你們看看它的方法就知道了:
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor { // 注意,before Instantiation的意思是,實例化以前調用 Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException; // 這個,實例化以後調用 boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException; ...無關方法 }
因此,InstantiationAwareBeanPostProcessor擴展了兩個時間點出來,一個是實例化前,一個是實例化後。
什麼叫實例化?把一個對象new出來,通俗來說,就叫作實例化。
在spring的getBean流程裏,實例化確定是早於初始化的。因此,一個bean,會經歷以下順序(你們直接看前面點的圖,更清晰):
InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation;
根據bean definition來實例化,假設bean class爲A,bean definition中的constructorArgumentValues參數,若是爲空,則使用A的默認構造函數;若是constructorArgumentValues有值,表示:在此以前,須要先獲取到相應的構造函數值,才能去反射經過構造器來建立對象
這裏面,會涉及到bean的遞歸調用。
好比,之前一篇和開頭提到的,AspectJPointcutAdvisor 這個bean definition來講,其中它的構造函數,就要求一個AbstractAspectJAdvice 對象:
public AspectJPointcutAdvisor(AbstractAspectJAdvice advice) { Assert.notNull(advice, "Advice must not be null"); this.advice = advice; this.pointcut = advice.buildSafePointcut(); }
那AbstractAspectJAdvice 這個對象要怎麼生成,這個在咱們的場景下,是被抽象成一個內部bean的,bean class爲AspectJAfterAdvice。AspectJAfterAdvice這個類呢,構造函數又是下面這樣的:
public AspectJAfterAdvice( Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) { super(aspectJBeforeAdviceMethod, pointcut, aif); }
又依賴了一堆其餘的參數,這三個參數,其中2個也是被定義爲了內部bean definition
,一個爲bean 引用。(具體請參照前一篇)。
因此,這個bean的實例化過程就相對繁瑣,涉及到bean的遞歸生成。
InstantiationAwareBeanPostProcessor的postProcessAfterInstantiation;
屬性注入,此時是autowired等發揮做用的地方;
BeanPostProcessor的postProcessBeforeInitialization
BeanPostProcessor#postProcessAfterInitialization
這裏面6個步驟,AspectJAwareAdvisorAutoProxyCreator 在其中兩個地方,實現了本身的業務邏輯。
具體實如今:
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { Object cacheKey = getCacheKey(beanClass, beanName); if (beanName == null || !this.targetSourcedBeans.contains(beanName)) { if (this.advisedBeans.containsKey(cacheKey)) { return null; } // 入口在這:shouldSkip if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return null; } } // Create proxy here if we have a custom TargetSource. // Suppresses unnecessary default instantiation of the target bean: // The TargetSource will handle target instances in a custom fashion. if (beanName != null) { TargetSource targetSource = getCustomTargetSource(beanClass, beanName); if (targetSource != null) { this.targetSourcedBeans.add(beanName); Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource); Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } } return null; }
以上邏輯中,咱們須要進入到shouldSkip方法,重點是下面的findCandidateAdvisors方法:
@Override protected boolean shouldSkip(Class beanClass, String beanName) { // TODO: Consider optimization by caching the list of the aspect names /** * 這裏調用了父類中實現的findCandidateAdvisors,獲取候選的advisor bean;這裏也是真正根據bean * definition去生成advisor bean的地方 */ List<Advisor> candidateAdvisors = **findCandidateAdvisors()**; for (Advisor advisor : candidateAdvisors) { /** * 若是當前要檢查的bean,就是advisor裏的通知類,則跳過 */ if (advisor instanceof AspectJPointcutAdvisor) { if (((AbstractAspectJAdvice) advisor.getAdvice()).getAspectName().equals(beanName)) { return true; } } } return super.shouldSkip(beanClass, beanName); }
下面會找出ioc容器中,實現了Advisor接口的bean definition,並所有實例化。Advisor是什麼?
咱們前面提到的AspectJPointcutAdvisor,就實現了這個接口。
public List<Advisor> findAdvisorBeans() { // Determine list of advisor bean names, if not cached already. String[] advisorNames = null; synchronized (this) { advisorNames = this.cachedAdvisorBeanNames; if (advisorNames == null) { // 1.從spring容器查找Advisor類型的bean definition advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Advisor.class, true, false); this.cachedAdvisorBeanNames = advisorNames; } } if (advisorNames.length == 0) { return new LinkedList<Advisor>(); } List<Advisor> advisors = new LinkedList<Advisor>(); for (String name : advisorNames) { if (isEligibleBean(name) && !this.beanFactory.isCurrentlyInCreation(name)) { // 遍歷那些bean definition,經過getBean,來獲取bean advisors.add(this.beanFactory.getBean(name, Advisor.class)); } } return advisors; }
再囉嗦一句,advisor差很少是aop的核心數據結構,你經過Aspect註解方式,最終也是解析爲一個個的Advisor。
一個切點+一個切面方法 基本就等於一個Advisor對象。好比,一個before方法,算一個;在一個after,又算一個。
這是第二個時間點,在這裏,檢查bean要不要被攔截,生成代理。你們能夠簡單理解,由於每一個bean的建立過程,都要被它處理,它呢,就會檢查,這個bean,是否是匹配切點,若是匹配,就生成代理。
舉個例子,之前看過一個小說,是京官去很偏遠的地方上任,路上被人殺了,並被另一我的拿了印章,到了地方,靠着印章,招搖撞騙,當了地方官。假設切點是:每一個過路人;切面:偷天換日。那這個PostProcessor,就是那夥人,攔截每一個過路人,並判斷是否是那個倒黴的京官,若是是,就殺了而且換我的拿了印章去當地方官。
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (!this.earlyProxyReferences.contains(cacheKey)) { // 返回生成的代理對象 return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }
這裏的下面這句,bean是原始bean,wrapIfNecessary返回的,就做爲最終的bean。若是你不須要切這個bean,那你就返回它自己;若是要代理,你就返回另外一個代理對象便可。
return wrapIfNecessary(bean, beanName, cacheKey);
咱們仔細看這個方法內部:
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { if (beanName != null && this.targetSourcedBeans.contains(beanName)) { return bean; } if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean; } if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } // 獲取前面2.1章節裏的Advisor對象,並保存到Object[] specificInterceptors 裏 Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors != null) { this.advisedBeans.put(cacheKey, Boolean.TRUE); // 建立代理,注意,Advisor數組,已經被傳進去了。 Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; }
你們看上面的註釋吧,這裏獲取了ioc容器裏的Advisor對象,若是advisor數組不爲null,則建立代理,並返回代理。你們看下面的debug圖就理解了。
建立代理的過程就簡單了,基本就是:有接口就搞個jdk 動態代理,不然就cglib代理。
我作了個小測試,
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { if (bean.getClass().getName().equals("foo.Performer")) { return "hahha"; } ...其餘代碼省略,主要加了上面那個if return bean; }
public final class Main { public static void main(String[] args) { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext( "context-namespace-test-aop.xml"); Perform performer = (Perform) ctx.getBean(Perform.class); performer.sing(); } }
而後在倒數第二行,獲取Perform類型的bean時,報錯了。爲啥呢?由於原本foo.Performer實現了Perform接口,但如今,我用一個string做爲代理返回去了,因此就沒有實現Perform接口的bean存在了
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [foo.Perform] 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:23)
spring aop這個東西,仍是不簡單,我原本打算這一講把所有內容說清楚;但如今發現,建立代理這部分,仍是得放到下一篇。
至於源碼那些,在第16篇裏給了地址的,你們能夠看看。有啥問題,及時聯繫我。