Spring-beans架構設計原理
IOC
IOC,大名鼎鼎,如雷貫耳。官方給的定義是依賴注入(Dependency Injection)或者控製反轉(Inversion of Control),都相當術語化,不太容易懂。
想象下日常中的生產過程,在生產之前是客戶下單,單子上會詳細注明需要的產品,包括產品的各方麵規格屬性,然後工廠據此生產。
IOC就是一個類似的過程,我們聲明需要什麼,工廠據此給我們生產出來。在這個過程中我們隻是給出了需求清單,而不用再去辛苦new,以及不停設置各種屬性。
Spring ioc
Spring-beans是ioc最著名的實現,而且沒有之一,在業界幾乎也就是ioc的代名詞。Beans是整個spring的基石組件,可以說所有的spring組件都是基於它。
spring-beans整體架構
Spring-beans提供的優秀的可擴展能力使spring幾乎能包容一切,用戶隻需遵循spring beans的相關規範--spring.schema定義配置文檔的文法規範,spring.handler定義客戶化配置的解析工具--就可以將bean接入到spring容器裏。Bean接入後還可以通過實現BeanPostProcessor或者init-method對bean做後處理甚至替換bean。
Spring-beans的優秀設計使spring越來越像是一個生態圈,基於beans,aop、context、mvc、annotation等強大的組件都被接入進來,除此還有一些優秀的第三方組件,例如dubbo等。
以aop為例,先在parse階段對aop的配置生成Advised bean(Advisor),然後在所有bean的後處理階段get Advised bean,並通過filter判斷是否適應於當前bean,適用則會對bean織入advice。後麵如果有時間會專門做篇aop,這裏不再繼續深入。
Dubbo和aop略有區別,dubbo的擴展選擇了init method中的afterPropertiesSet,所有嚴格意義上dubbo是無法去spring的,雖然dubbo聲明是可以,但其實隻能不適用spring功能,而不能去spring jar,dubbo bean在init階段生成ref,其實也是個代理--封裝了遠程訪問的細節。
Bean定義
Spring-beans的核心實體是BeanDefinition和BeanFactory。前者映射我們的定義,後者則是依據定義生產bean的工廠。
上圖是spring beans的靜態結構圖,更多是偏重於bean解析,因為1. 理解了bean解析也就理解了一半spring擴展能力;2.BeanFactory的複雜不在於類之間的組織結構,而在於複雜的調用鏈路,也就沒必要是靜態結構方麵做過多說明。需要說明的是,這隻是概念模型,並不完全映射到類,因為spring的抽象層次太高,一個概念實體功能往往由多個類協同完成,畫起來比較費勁,就類似BeanFactory,光搞清楚各個BeanFactory之間的關係就理得頭痛,所以都盡可能從概念層麵說明。
重要實體說明
- DefaultListableBeanFactory是BeanDefintionRegistry的默認實現,它是個適配器,用於適配BeanFactory和BeanDefintionRegistry,工廠和定義通過它統一。它由ApplicationContext初始化,並被作為BeanDefinitionReader的registry。Reader對配置文檔加載解析,生成definition並注冊到registry--其實就是DefaultListableBeanFactory,這樣工廠就擁有了類定義,bean初始化時也可以通過內部方法輕鬆獲取到定義。
- NamespaceHandlerResolver用於獲取配置解析實體--NamespaceHandler。它和registry均內聚在上下文實體--ReaderContext中,parser內聚上下文從而可以間接訪問handler和registry獲得解析和注冊的能力。
其他幾個實體都比較直觀便於理解,不再一一贅述。
整體交互過程
BeanDefinitionReader是整個bean解析的聚合根,它由ApplicationContext創建,並將DefaultListableBeanFactory作為registry傳遞給它。
BeanDefinitionReader創建文檔讀取實體--DocuemntReader用於加載解析,並在step3加載文檔時創建上下文--ReaderContext傳遞給文檔讀取實體。上下文貫穿於整個解析過程始終,它在文檔讀取實體使用parser解析時也會被傳入parser中。
Parse過程是整個加載過程的核心,默認parser通過間接關聯的識別器可以依據不同配置節點進行parser切換,當讀到非默認配置時,則切換到對應客戶化parser解析。解析完成後再通過間接關聯的registry進行注冊,從而配置定義進入spring管理,待getBean時使用。
客戶化配置節點解析
客戶化配置是spring非常重要的擴展點,spring強大的擴展能力有一半功能要歸功於它,另一半中的80%就是後麵要介紹的大名鼎鼎的BeanPostProcessor。不僅僅一些第三方擴展(例如開篇提到的dubbo)基於它,spring本身的很多模塊也是基於它,例如spring-aop,spring-context等等,spring體係內除了默認的beans命名空間其餘都基於它擴展的。
NamespaceHandlerResolver由BeanDefinitionReader初始化,後者在第一次被訪問時讀取spring.handlers文件。.handlers文件定義namespace uri和對應處理類的映射關係。例如:
http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler
上麵的這行配置就是配置聲明的解析類。
NamespaceHandlerResolver依據節點namespace獲得NamespaceHandler,然後使用handler處理自定義配置節點。
public interface NamespaceHandler {
void init();
BeanDefinition parse(Element element, ParserContext parserContext);
BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder definition, ParserContext parserContext);
}
init方法注冊localName和自定義parser的關係,parser和localName的關係由handler的提供者自己注冊。例如:
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
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}
上麵就是aop注冊parser的代碼片段。config,aspectj-autoproxy這些就是localName,parse過程中不同的localName會切換到不同的parser解析。
spring先通過命名空間定位到handler,handler處理時再基於localName取相應的parser解析當前結點。比如這個配置,aop是命名空間,aspectj-autoproxy是localName。整個讀取解析過程中先通過aop找到AopNamespaceHandler,再在解析到aspectj-autoproxy節點時使用AspectJAutoProxyBeanDefinitionParser來解析。如果要研究spring源碼,一定要先找到對應parser,知道每個配置項對應到運行時的bean結構才能更好理解spring;而且parser可能會生成一些默認的BeanPostProcessor,如果意識不到這些後處理器,那麼對代碼的讀取將會斷片,陷入完全無法理解的境地。比如spring-aop就是由parser默認生成AopAutoProxyCreator這個BeanPostProcessor,在bean初始化後由這個processor對bean生成代理。
Bean獲取
上圖是getBean過程,整個過程很簡潔,實際深入代碼會發現非常繁瑣。
BeanFactory和BeanDefinitionRegistry在spring裏是統一的,參見第一節,圖上為了方便理解,拆成兩個概念實體。
需要注意的是第4步和第6步,bean配置時可以指定parent屬性,如果有parent,則beanFactory會對local和parent做merge,merge的策略是對parent做覆蓋,也可以理解為是對parent做繼承。這和parent bean factory完全是兩個概念,一定要區分開。
在beans的實體靜態結構裏,分別注明了parent bean definition和parent bean factory。兩者都是被關聯的,而不是被繼承。後者有點像jvm的雙親委托模型,parent和child有各自的上下文,類似於jvm的命名空間。parent bean factory由applicationContext設置,無法配置。比如spring mvc就是兩個父子兩個容器,在容器refresh時相應的也會把父容器的BeanFactory設置成子容器BeanFactory的parentBeanFactory。
spring bean狀態
Bean主要經過instantiate,populate,initializeBean和registerDisposableBean4個狀態,在狀態流轉中會調用很多spring預留的擴展接口。
awareMethod
如果bean繼承了BeanFactoryAware,BeanNameAware,BeanClassLoaderAware,則會在initialize階段將BeanFactory, BeanName和bean ClassLoader設置給Bean。
注意它和ApplicationContextAware是不一樣的,後者是由BeanPostProcessor做後處理set的。init method
init method不僅僅包括配置的init-method方法還包括InitializedBean的afterPropertiesSet回調接口,這兩者均是無參的,完全可以互相替代,兩者中afterPropertiesSet調用在前。
BeanPostProcessor
上文提到過,spring另一半的擴展能力是由BeanPostProcessor提供的。先看下其接口定義
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
兩個方法分別在init-method調用前後,對bean做後處理。需要特別注意的就是postProcessAfterInitialization,大部分的spring擴展就是由它來完成的,比如上文提到的aop就是在這個階段對bean做後處理生成代理。相應的也可以使用postProcessBeforeInitialization,但是此時init-method並未執行,後處理需要保證init-method帶來的影響,@PostConstruct的方法執行就是在這個階段。
實例化後處理器
InstantiationAwareBeanPostProcessor繼承了BeanPostProcessor,主要用於在bean實例化前後做處理。
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException;
boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException;
PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)
throws BeansException;
}
- 實例化前處理,這個方法的參數是beanClass和beanName,在bean實例化前調用,如果這個方法的返回值不為空則getBean結束,用戶收到的就是這個該方法的返回值。這個擴展點主要是留給預處理的,用戶可以直接生成一個同類型的bean,替換實際bean,此時實際bean不會被實例化。
- 實例化後處理連同屬性後處理是spring內部非常重要的擴展點,annotation的field注入就是在這兩個階段完成。主要有兩個關鍵實現: CommonAnnotationBeanPostProcessor,該類在實例化後處理階段做屬性注入,主要用於@Resource AutowiredAnnotationBeanPostProcessor,該類在屬性後處理階段做屬性注入,主要用於@Autowired 前者還繼承了InitDestroyAnnotationBeanPostProcessor類,該類會在init-method被執行前調用@PostConstruct方法。
最後更新:2017-06-05 11:33:02