Spring IOC和Spring AOP的實(shí)現(xiàn)原理(源碼主線流程)
Spring IOC
一、容器初始化
? 容器的初始化首先是在對(duì)應(yīng)的構(gòu)造器中進(jìn)行三痰,在applicationContext的實(shí)現(xiàn)類構(gòu)造器中,首先對(duì)參數(shù)路徑中的${}進(jìn)行了處理窜管,用系統(tǒng)變量替換(setConfigLocations方法)然后調(diào)用refresh方法(這個(gè)就是最核心的容器初始化方法)酒觅。
1、Resource定位:
? 在refresh方法中調(diào)用obtainFreshBeanFactory方法告訴子類刷新beanfactory(其中是調(diào)用refreshBeanFactory刷新后getBeanFactory獲取刷新后的factory返回)微峰。在刷新過(guò)程refreshBeanFactory中如果factory已經(jīng)有了要消除再新建factory,其中l(wèi)oadBeanDefinitions是加載bean定義的方法抒钱。
? 在loadBeanDefinitions方法中創(chuàng)建了BeanDefinitionReader的實(shí)現(xiàn)類調(diào)用其loadBeanDefinitions方法(這個(gè)方法是重載方法蜓肆,參數(shù)有為Resource的也有為String路徑的,getConfigResources方法(默認(rèn)返回null谋币,子類重寫仗扬,如ClassPathXmlApplicationContext類)和getConfigLocations方法獲得Resource集合和資源路徑集合(一般一個(gè)為空,一般是將容器的參數(shù)path設(shè)定為configLocations蕾额,ClassPathXmlApplicationContext有一種構(gòu)造器是不設(shè)定configLocations而是直接用參數(shù)path生成ClassPathResource集合設(shè)定為configResources)分別進(jìn)行l(wèi)oad早芭,實(shí)際上以路徑為參數(shù)的重載方法在定位完Resource也會(huì)調(diào)用以resource為參數(shù)的loadBeanDefinitions來(lái)解析載入BeanDefinition,這個(gè)是第二步在下面介紹)诅蝶。
? 在BeanDefinitionReader的loadBeanDefinitions(path參數(shù))方法中根據(jù)ResourceLoader類型以兩種方式加載(如果是ant正則表達(dá)式方式的(如PathMatchingResourcePatternResolver)一個(gè)路徑定位多個(gè)resource或者默認(rèn)方式(applicationContext繼承的是DefaultResourceLoader實(shí)現(xiàn)方式)定位一個(gè)resource)退个,分別調(diào)用ResourceLoader的getResource(以/開(kāi)頭的構(gòu)建ClassPathContextResource,以classpath開(kāi)頭的去掉classpath構(gòu)建ClassPathResource调炬,如果都不是的嘗試構(gòu)建UrlResource,如果構(gòu)建失敗就調(diào)用getResourceByPath這個(gè)具體applicationContext實(shí)現(xiàn)類里重寫的方法構(gòu)建特定Resource语盈,如FileSystemXmlApplicationContext就是FileSystemResource)或getResources(PathMatchingResourcePatternResolver的正則方式這里不詳細(xì)描述)完成Resource定位。
2缰泡、從Resource中解析和載入BeanDefinition:
? 同樣在BeanDefinitionReader的loadBeanDefinitions中調(diào)用完resourceLoader的getResource獲取Resource后將resource作為參數(shù)調(diào)用自己(BeanDefinitionReader)的loadBeanDefinitions(是一個(gè)接口方法給子類實(shí)現(xiàn)刀荒,因?yàn)椴煌膔eader加載resource的方式不同)載入BeanDefinition。
? 例如XmlBeanDefinitionReader是對(duì)XML文件的IO操作,(將現(xiàn)在要處理的Resource加入當(dāng)前線程正在處理(ThreadLocal)的Resource集合中)首先從resource中拿出InputStream封裝成InputSource調(diào)用自身的doLoadBeanDefinitions方法棘钞。
? doLoadBeanDefinitions方法中調(diào)用doLoadDocument方法封裝成Document-----是用validationMode(默認(rèn)是自動(dòng)校驗(yàn)方式缠借,意思是如果沒(méi)有顯示定義校驗(yàn)的方式就用XSD方式)和DocumentLoader(XmlBeanDefinitionReader中默認(rèn)的是DefaultDocumentLoader)等參數(shù)調(diào)用DocumentLoader的loadDocument方法將Resource封裝成Document類(具體封裝方式不做介紹,有興趣的可以自己了解一下宜猜,用的是builder模式做的)調(diào)用registerBeanDefinitions方法解析載入bean泼返。
? registerBeanDefinitions方法是用BeanDefinitionDocumentReader的registerBeanDefinitions具體解析Document(樹(shù)形結(jié)構(gòu),從root(就是beans標(biāo)簽)開(kāi)始往下解析)中每個(gè)element各個(gè)標(biāo)簽的解析和載入宝恶。其中如果是bean標(biāo)簽BeabDefinitionParserDelegate的parseBeanDefinitionElement方法對(duì)XML元素的信息按照spring的bean的規(guī)則進(jìn)行解析(property的解析符隙,當(dāng)中value和ref解析方式不同趴捅,如果是value構(gòu)建TypedStringValue, 如果ref的話構(gòu)建RuntimeBeanReference,這個(gè)在之后依賴注入的時(shí)候用到霹疫,還有id拱绑,name,等屬性的解析)得到的BeanDefinition的封裝BeanDefinitionHolder(包括BeanDefinition,beanName(這里是標(biāo)識(shí)符的意思丽蝎,如果有id猎拨,id做標(biāo)識(shí)符,沒(méi)有id屠阻,name屬性中第一個(gè)別名做標(biāo)識(shí)符)和別名列表(name屬性中的內(nèi)容红省,如果沒(méi)有id,name中第一個(gè)不作為別名而是標(biāo)識(shí)符))來(lái)進(jìn)行下一步bean的注冊(cè)(BeanDefinitionReaderUtils.registerBeanDefinition)国觉。
? 其他如import吧恃,alias等標(biāo)簽自行看源碼理解。
3麻诀、BeanDefinition在IOC容器的注冊(cè)
? BeanDefinitionReaderUtils.registerBeanDefinition用BeanDefinitionRegistry(DefaultListableBeanFactory)的registerBeanDefinition方法注冊(cè)beanName和BeanDefinition(就是把beanName加入到一個(gè)已經(jīng)注冊(cè)的bean的beanName的Set中痕寓,然后put到beanName對(duì)應(yīng)BeanDefinition的map中,其中如果不允許覆蓋并且有同名beanName要報(bào)錯(cuò))蝇闭。再用BeanDefinitionRegistry的registerAlias方法注冊(cè)beanName和別名列表(put到一個(gè)beanName對(duì)應(yīng)alias的map中呻率,其中如果有alias跟beanName相同的要移除)。
二呻引、IOC容器依賴注入
1礼仗、getBean第一次調(diào)用lazy-init的bean
? 是以BeanFactory的getBean方法為入口觸發(fā)的(實(shí)現(xiàn)在AbstractBeanFactory實(shí)現(xiàn)類中)。如果是單例會(huì)緩存起來(lái)只加載一次逻悠,如果是FactoryBean這種特殊的bean會(huì)把這個(gè)bean的實(shí)例傳入getObjectForBeanInstance方法獲得FactoryBean產(chǎn)生的bean(調(diào)用FactoryBean的getObject方法元践,這就是自定義的FactoryBean要重寫的方法,AOP也是這個(gè)原理童谒,詳情見(jiàn)下方AOP分析)卢厂。在第一次載入時(shí)要先判斷這個(gè)BeanDefinition在當(dāng)前BeanFactory有沒(méi)有,沒(méi)有就從雙親BeanFactory中找惠啄,一直遞歸慎恒。
? 找到后要驗(yàn)證是否存在遞歸依賴,有則報(bào)錯(cuò)無(wú)則設(shè)置當(dāng)前bean依賴bean的依賴關(guān)系到兩個(gè)map中(一個(gè)是被依賴map撵渡,一個(gè)是依賴map)融柬,其中:? (1)如果是單例第一次載入就調(diào)用getSingleton方法(方法中回調(diào)了參數(shù)中ObjectFactory的getObject方法,這里重寫了這個(gè)方法調(diào)用createBean)獲得實(shí)例用getObjectForBeanInstance獲得FactoryBean產(chǎn)生的bean(如果它是FactoryBean的話)趋距。? (2)如果是prototype加載調(diào)用createBean后調(diào)用getObjectForBeanInstance粒氧。? (3)如果是其他scope類型:request、session和global session,這三種就用scope.get獲取實(shí)例(和getSingleton類似回調(diào)重寫的getObject也就是調(diào)用createBean)后調(diào)用getObjectForBeanInstance节腐。
? 最后如果getBean指定了requiredType要檢驗(yàn)獲取的bean能不能轉(zhuǎn)化成指定的類型不能的話就報(bào)錯(cuò)外盯。
? createBean方法就是生成bean的方法并對(duì)一些比如init-method屬性摘盆、后置處理器等一些初始化進(jìn)行了處理。方法中在實(shí)例化之前判斷是否有post-processor饱苟,如果有這樣的processor則短路指定bean的創(chuàng)建孩擂,直接返回一個(gè)proxy而不是指定的bean(這種processor可以指定生成一個(gè)其他類型的對(duì)象)沒(méi)有的話用doCreateBean創(chuàng)建bean返回。
? doCreateBean是用createBeanInstance生成BeanWrapper(包裝bean)之后用populateBean向其中的bean完成依賴bean的注入(autowire等)箱熬。
? createBeanInstance創(chuàng)建beanWrapper時(shí)分三類進(jìn)行處理:? (1)如果有工廠方法类垦,調(diào)用instantiateUsingFactoryMethod創(chuàng)建。? (2)如果是構(gòu)造器注入的方式調(diào)用autowireConstructor城须。? (3)簡(jiǎn)單方式調(diào)用instantiateBean蚤认。調(diào)用的是策略類(默認(rèn)SimpleInstantiationStrategy)的instantiate而其中又是通過(guò)bean方法是否有跟IOC容器同名的(會(huì)被覆蓋)來(lái)分兩類處理(沒(méi)同名方法的從BeanDefinition中拿出class直接用jdk的反射拿構(gòu)造器來(lái)newinstance一個(gè)實(shí)例,如果有同名的則是用CGLIB的方式來(lái)new一個(gè)實(shí)例)糕伐。
? populateBean為生成的bean依賴注入砰琢,先對(duì)非簡(jiǎn)單類型屬性有autowire的進(jìn)行處理,判斷這個(gè)屬性在之前解析載入beanDefinition時(shí)property里有沒(méi)有良瞧,有的話進(jìn)行g(shù)etBean初始化后放入PropertyValue集合中(這個(gè)就是propertyname和value的封裝)氯析,然后更新依賴map,再對(duì)非autowire的或一般屬性進(jìn)行注入莺褒,有要轉(zhuǎn)化的要經(jīng)過(guò)valueResolver的轉(zhuǎn)化(如果是RuntimeBeanReference之前載入時(shí)XML中配置是ref的就getBean(如果在雙親BeanFactory中就從雙親中取)獲得后也放到PropertyValue集合中,也要更新依賴map)雪情。最后再注入到bean中遵岩,這里說(shuō)的注入其實(shí)真實(shí)發(fā)生在最后的BeanWraper的setPropertyValue(propertyValue集合)方法,具體實(shí)現(xiàn)就是通過(guò)反射的方式獲得setter方法賦值巡通。
2尘执、lazy-init==false初始化(只對(duì)singleton,也是默認(rèn)方式)
? 在refresh方法中的finishBeanFactoryInitialization方法中進(jìn)行初始化(實(shí)際也是調(diào)用getBean方法)宴凉。
Spring AOP
? ProxyFacotryBean是FacotryBean的一種實(shí)現(xiàn)誊锭,F(xiàn)acotryBean要產(chǎn)生bean都要重寫getObject方法,而ProxyFacotryBean這里的這個(gè)getObject正是為代理做了準(zhǔn)備并返回代理對(duì)象。首先用initializeAdvisorChain(第一次去取代理對(duì)象時(shí)初始化一遍)初始化Advisor鏈后對(duì)于singleton和prototype進(jìn)行區(qū)分生成對(duì)應(yīng)的proxy弥锄。
1丧靡、初始化Advisor鏈
? initializeAdvisorChain初始化Advisor鏈?zhǔn)潜闅vProxyFacotryBean中配置的interceptorNames,如果結(jié)尾有通配符只能是ListableBeanFacotory來(lái)加載否則報(bào)錯(cuò)籽暇,去掉結(jié)尾通配符*后調(diào)用addGlobalAdvosor(這個(gè)是獲取ListableBeanFacotory的所有g(shù)lobalAdvisorNames和globalInterceptorNames温治,分別遍歷用getBean(beanName)獲取advice,把其中符合通配符格式的advice調(diào)用addAdvisorOnChainCreation封裝成advicsor后添加到Advisor鏈戒悠,如果結(jié)尾沒(méi)有通配符的情況下無(wú)論是singleton還是prototype在獲得advice后都要用addAdvisorOnChainCreation方法注冊(cè)到advisor鏈上熬荆。
? addAdvisorOnChainCreation用namedBeanToAdvisor方法把a(bǔ)dvice包裝成advisor,判斷如果advice是單例singleton的話是用AdvisorAdapterRegistry(默認(rèn)DefaultAdvisorAdapterRegistry單例)wrap方法判斷如果這個(gè)advice是MethodInterceptor或者AdvisorAdapterRegistry三種固定的adapter(before,afterreturning,throws)如果任一adapter支持的話(支持不支持就是在具體的adapter中判斷advice是不是這個(gè)adapter對(duì)應(yīng)具體的advice類的子類)就封裝成DefaultPointcutAdvisor返回绸狐。如果是prototype的話不獲取getBean卤恳,而是直接用name包裝成PrototypePlaceholderAdvisor累盗。
2、生成代理類
? 以singleton為例突琳,singleton代理的生成getSingletonInstance方法若债。是用AopProxyFactory(在構(gòu)造器中設(shè)定了默認(rèn)的DefaultAopProxyFactory)的createAopProxy方法根據(jù)ProxyFacotryBean中配置的target判斷是否是個(gè)接口(實(shí)際上不是這么簡(jiǎn)單的區(qū)分,具體看源碼了解)來(lái)創(chuàng)建不同AopProxy的子類(JdkDynamicAopProxy或者ObjenesisCglibAopProxy(CglibAopProxy的子類本今,增加了ObjenesisStd))調(diào)用他們各自的getProxy方法以不同的方式創(chuàng)建代理對(duì)象返回拆座。
? JdkDynamicAopProxy就是以動(dòng)態(tài)代理的方式構(gòu)建代理對(duì)象返回(具體動(dòng)態(tài)代理原理自行了解哦)。
? CglibAopProxy就是以Cglib的方式進(jìn)行代理冠息,Cglib采用了非常底層的字節(jié)碼技術(shù)挪凑,其原理是通過(guò)字節(jié)碼技術(shù)為一個(gè)類創(chuàng)建子類,并在子類中采用方法攔截的技術(shù)攔截所有父類方法的調(diào)用逛艰,順勢(shì)織入橫切邏輯躏碳。具體細(xì)節(jié)超出這文章的范圍拉。
? prototype代理的方式大致相同有些許的差別也不做介紹散怖,可以參考源碼菇绵。
3、調(diào)用時(shí)攔截
? 在調(diào)用目標(biāo)類的方法時(shí)因?yàn)榇碚{(diào)用的是invoke(jdk動(dòng)態(tài)代理)或者intercept(cglib)镇眷。在invoke(jdk動(dòng)態(tài)代理)或者intercept(cglib)中根據(jù)目標(biāo)類被調(diào)用方法分別處理咬最。
? 如果是hashCode和equals方法直接調(diào)用代理類中重寫了的hashCode和equals方法(具體參考源碼)。? 如果是Adviced接口中定義的方法(ProxyFactoryBean就是Adviced接口實(shí)現(xiàn)類)直接以反射的方式拿到method調(diào)用方法(AopUtils的invokeJoinpointUsingReflection方法)欠动。? 其他情況就是拿到攔截器鏈(只初始化一次永乌,每次調(diào)用時(shí)有個(gè)currentInterceptorIndex記錄處理到第幾個(gè)攔截器)調(diào)用攔截器的proceed方法前進(jìn)調(diào)用。
? proceed前進(jìn)調(diào)用不是遞歸具伍,其中用matcher進(jìn)行匹配翅雏,如果匹配上調(diào)用攔截器的invoke方法,匹配不上就直接繼續(xù)前進(jìn)調(diào)用人芽,攔截器interceptor的invoke方法就是通知方法(自己實(shí)現(xiàn)的如afterReturning等)對(duì)目標(biāo)方法(實(shí)際是攔截器鏈的proceed前進(jìn)調(diào)用)的具體加強(qiáng)望几,就是順序問(wèn)題等等。
? 直到攔截器鏈前進(jìn)到底調(diào)用target目標(biāo)類的對(duì)應(yīng)方法(jdk反射獲取method調(diào)用)萤厅。
? 初始化攔截器鏈?zhǔn)峭ㄟ^(guò)遍歷之前IOC容器getBean獲取到advisor鏈中的Advisor橄抹,通過(guò)AdvisorAdapterRegistry當(dāng)中設(shè)置的3種adapter(before,afterreturning,throws)的supportsAdvice判斷是否支持該advisor,如果支持就將advisor中的advice注冊(cè)成不同的AdviceInterceptor列表(一個(gè)advisor可以被多個(gè)adapter支持惕味,因?yàn)橹灰约簩懙耐ㄖ悓?shí)現(xiàn)多種advice接口即可)都加入到攔截器鏈害碾。