您好,登錄后才能下訂單哦!
本篇內容主要講解“Spring BeanDefinition的加載過程”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Spring BeanDefinition的加載過程”吧!
?XML配置的加載由AbstractXmlApplicationContext實現,方法實現如下:
?主要是實例化了一個XmlBeanDefinitionReader對象,對其設置了Environment對象(具體過程可以上一篇)等后,調用XmlBeanDefinitionReader進行解析。直接跟蹤進去,得到如下內容:
?其中參數inputSource為XML配置文件,Resource則該配置文件的描述,主要描述了該配置文件在classpath中的位置和可用于加載該配置文件的加載器。doLoadDocument方法比較簡單,主要是加載指定的配置文件,返回一個JDK內置的XML解析Document對象,以便于解析XML DOM節點。
?重點看下registerBeanDefinitions方法,如下:
?該方法最終委托給了BeanDefinitonDocumentReader來完成Bean的解析。在這之前,初始化了一系列相關對象。包括:
使用DefaultBeanDefinitionDocumentReader作為BeanDefinitionDocumentReader接口的實現
創建了一個XmlReaderContext用于保存解析過程中使用到的各個相關對象,如資源描述對象Resource、ReaderEventListener事件監聽器、XmlBeanDefinitionReader以及NamespaceHandlerResolver命名空間解析器。其中劃重點的是DefaultNamespaceHandlerResolver,該類完成了自定義XML格式的解析,后面會有講解。
?初始相關對象后便將解析過程委托給了DefaultBeanDefinitionDocumentReader來進行處理,該類的重點為parseBeanDefinitions方法,在調用該方法前,先初始化了一個BeanDefinitionParserDelegate對象,如下:
?并將該delegate對象傳入了parseBeanDefinitions方法。BeanDefinitionParserDelegate主要提供了命名空間為http://www.springframework.org/schema/beans
(下面簡寫為beans空間)的XML文件解析過程。該命名空間定義了4個主要的XML標簽,分別為beans
、bean
、import
和alias
以及這些標簽對應的屬性,如下為該命名空間的示例,定義了一個基礎的Bean。
?需要注意的是,上面在初始化BeanDefinitionParserDelegate后會先解析XML上<beans>
標簽上的默認屬性,包括:default-lazy-init
、default-merge
、default-autowire
、default-dependency-check
、default-autowire-candidates
、default-init-method
和default-destroy-method
這些全局屬性。
?下面看下parseBeanBefinitoins方法:
?該方法會對當前節點所屬命名空間進行判斷,分為默認命名空間和自定義命名空間,其中默認命名空間指的是上面提到的beans空間。對于默認命名空間,會逐一解析每個DOM子節點,判斷子節點的命名空間,最終委托給兩個方法:處理默認命名空間的parseDefaultElement方法和處理自定義命名空間的parseClustomElement方法。
?parseDefautlElement方法如上, 對beans空間定義的各個標簽分別進行了處理:
解析import標簽時,會讀取resource屬性指定的配置文件,加載后再解析該文件中的bean定義。
解析alias標簽時,讀取標簽的name和alias屬性,添加到BeanRegistry緩存中。
解析bean標簽時,直接委托給BeanDefinitionParserDelegate來處理,過程為:
對于步驟3.d
,處理過程為:
獲取class屬性值
獲取parent屬性值
初始化GenericBeanDefinition實例
解析bean節點的屬性,設置到BeanDefinition中,包括:scope
、abstract
、lazy-init
、autowired
、dependency-check
、autowire-candidate
、primary
、init-method
、destroy-method
、factory-method
、factory-bean
解析description子節點,獲取值設置bean的描述內容
解析meta子節點列表,獲取key、value值設置附加元數據信息
解析lookup-method子節點列表,獲取name、bean值設置方法注入信息
解析replaced-method子節點列表,獲取值設置需要動態代理的方法信息
解析constructor-arg子節點列表,獲取值設置構造參數信息
解析property子節點列表,獲取值設置屬性信息
解析qualifier子節點列表,獲取值進行設置
獲取id屬性值作為beanName
獲取name屬性值作為aliases,該屬性值可以配置多個,以 , 或者 ; 符進行分割,將作為該bean的別名使用;若id值為空,且name不為空,則使用第一個name值作為id值
檢查beanName和aliases的唯一性
解析bean其他配置,生成GenericBeanDefinition對象
若beanName為空,則為其分配一個
?上面解析完bean的配置后,會再處理子節點中其他命名空間的配置,使用NamespaceHandler的decorate方法,用以修改Bean定義內容,這部分將使用下面的內容,放在后面一起講。
解析beans標簽時,會進行遞歸處理
?如上,默認命名空間主要用于解析bean的定義,經過上面的處理,bean定義的解析就已經完成,會將實例對象注冊到上下文中進行保存
?下面介紹parseClustomElement方法,顧名思義,該方法主要用來處理自定義命名空間的XML標簽的,可以當做是spring XML配置處理的一種擴展手段,如下,為該方法的內容:
?主要委托給了NamespaceHandlerResolver,通過查找到對應節點對應命名空間的Handler,調用該Handler的parse方法進行處理。
?前面說過,NamespaceHandlerResolver使用了DefaultNamespaceHandlerResolver
作為實現,跟蹤resolve方法進去如下:
?過程為:
獲取已有的Handler處理列表,返回結果為一個Map,Key為XML命名空間,值可能為代表對應Handler類型的Spring對象或者已經實例化后的Handler對象,取決于之前是否已經調用過
若指為Handler對象,則直接返回
若為String對象,表明未初始化過,則初始化該類,并執行init初始化方法,然后將其重新返回Map中
?對于第(1)步中Handler處理列表的獲取,Spring會掃描classpath中所有位于META-INF中的spring.handlers配置文件,將所有內容讀取到一個Map中并返回。
?如上,為spring-context模塊提供的spring.handlers
文件,提供了該模塊自定義命名空間標簽的支持。如下為自定義命名空間的例子:
?主要引入了context空間的spring-configured
標簽和annotation-config
標簽。
?至此,介紹了XML配置下的bean解析。
?下面介紹Spring以注解的方式進行bean加載的過程,如下,為開啟注解加載所需要的配置:
?根據前面的內容,component-scan
為http://www.springframework.org/schema/context
命名空間中的標簽,處理對象在spring-context模塊的spring.handlers文件中定義,對應的是類org.springframework.context.config.ContextNamespaceHandler,如下:
?查看該類,可以知道,component-scan由ComponentScanBeanDefinitionParser處理,如下:
?主要過程為:
獲取base-package
屬性內容賦值給basePackage
替換basePackage中的占位符內容
根據 , ; \t \n 分割符分割basePackage,得到多個包路徑
解析component-scan配置內容,返回ClassPathBeanDefinitionScanner對象
解析設置use-default-filters參數
解析設置resource-pattern參數
解析設置name-generator參數
解析設置scope-resolver、scoped-proxy等參數
解析設置include-filter
、exclude-filter
等參數, ClassPathBeanDefinitionScanner對象再初始化時默認增加了org.springframework.stereotype. Component、javax.annotation.ManagedBean和javax.inject.Named幾種注解
調用ClassPathBeanDefinitionScanner的doScan方法執行掃描,將符合的類并注冊到上下文中然后返回
解析annotation-config參數,如果為true(默認為true)則自動注冊一系列用于后置解析的注解處理類定義到上下文中 這里重點看下第(5)步和第(6)步
?第(5)執行掃描時,會遍歷第(3)步返回的所有路徑,對于每個路徑,會調用父類ClassPathScanningCandidateComponentProvider的findCandidateComponents方法,返回該路徑下所有符合要求的Bean,如下:
?ClassPathScanningCandidateComponentProvider會找到指定路徑所有的類,包裝為Resource[]對象,對于每個Resource,會使用SimpleMetadataReaderFactory工廠類為每個Resouce對象新建一個SimpleMetadataReader對象,該對象用于解析類的信息,需要注意的是,在初始化SimpleMetadataReader對象的時候就會執行解析動作,將結果存為ClassMetadata數據和AnnotationMetadata數據,前者用于存儲類的定義信息,包括類名,是否接口,包含的屬性等信息,后者則包含所有的注解信息。獲取SimpleMetadataReader對象后,會判斷該類是否符合component-scanner
定義的include-filter
和exclude-filter
中定義的內容,注意,默認包含了@Component
等對象,所以默認會加載所有有@Component
注解且所有有@Component
元注解注解(如@Service
、@Repository
)的類。若符合要求,則將該類包裝為ScannedGenericBeanDefinition對象,同時會檢查該類不能為一個接口且不能依賴一個內部非靜態類,若符合,則添加到待返回列表中。
?執行完上面的findCandidateComponents方法后,會為其分配一個beanName,用于內部使用,之后會調用AnnotationConfigUtils的processCommonDefinitionAnnotations方法,該方法會對上面返回的BeanDefinition解析一些基本的注解屬性并進行設置,包括@Lazy
、@Primary
、@DependsOn
、@Role
和@Description
。完成該步后會判斷該bean是否已經存在,若不存在,則添加到上下文中。
?第(6)步的代碼如下:
?重點在后半部分,會調用AnnotationConfigUtils的registerAnnotationConfigProcessors方法,該方法添加了一系列用于后置解析的注解處理類定義到上下文中,包括:
添加ConfigurationClassPostProcessor處理器(BeanDefinitionRegistryPostProcessor),添加@Configuration
功能,對應bean為org.springframework.context.annotation.internalConfigurationAnnotationProcessor
添加AutowiredAnnotationBeanPostProcessor處理器(SmartInstantiationAwareBeanPostProcessor、MergedBeanDefinitionPostProcessor),添加@Autowired
,@Value
功能,對應bean為org.springframework.context.annotation.internalAutowiredAnnotationProcessor
添加RequiredAnnotationBeanPostProcessor處理器(SmartInstantiationAwareBeanPostProcessor、MergedBeanDefinitionPostProcessor),添加@Required
功能,對應bean為org.springframework.context.annotation.internalRequiredAnnotationProcessor
添加CommonAnnotationBeanPostProcessor處理器(InstantiationAwareBeanPostProcessor 、DestructionAwareBeanPostProcessor、MergedBeanDefinitionPostProcessor、BeanPostProcessor),添加@PostConstruct
、@PreDestroy
功能,對應bean為org.springframework.context.annotation.internalCommonAnnotationProcessor
添加PersistenceAnnotationBeanPostProcessor處理器(InstantiationAwareBeanPostProcessor、DestructionAwareBeanPostProcessor、MergedBeanDefinitionPostProcessor),添加@PersistenceUnit
、@PersistenceContext
功能,對應bean為org.springframework.context.annotation.internalPersistenceAnnotationProcessor
添加EventListenerMethodProcessor,添加@EventListener
功能,對應bean為org.springframework.context.event.internalEventListenerProcessor
添加DefaultEventListenerFactory,對應bean為org.springframework.context.event.internalEventListenerFactory
?以上各個bean在添加前都會先判斷是否已經存在改定義,若不存在才增加,因而可以通過在添加相應bean的方式,修改對應的處理功能。
?PS:第(6)步添加注解后置處理的方法其實也是annotation-config這個標簽功能的主要處理方法。由上可知annotation-config的處理類為AnnotationConfigBeanDefinitionParser,該類內部其實也是調用了AnnotationConfigUtils的registerAnnotationConfigProcessors方法來完成注解的功能,具體代碼如下:
?結合之前Spring啟動的內容,接上上面的內容,可以得到如下的接口回調順序
?因為InstantiationAwareBeanPostProcessor、DestructionAwareBeanPostProcessor等接口繼承自MergedBeanDefinitionPostProcessor接口,MergedBeanDefinitionPostProcessor接口繼承自BeanPostrProcessor,實現類上存在重疊,這里先根據講解順序排序。
到此,相信大家對“Spring BeanDefinition的加載過程”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。