前面提到AbstractRefreshableApplicationContext在刷新BeanFactory時,會調用loadBeanDefinitions方法以加載系統中Bean的定義,下面將講解Bean定義的加載過程。java
XML配置的加載由AbstractXmlApplicationContext實現,方法實現以下:spring
主要是實例化了一個XmlBeanDefinitionReader對象,對其設置了Environment對象(具體過程能夠上一篇)等後,調用XmlBeanDefinitionReader進行解析。直接跟蹤進去,獲得以下內容:segmentfault
其中參數inputSource爲XML配置文件,Resource則該配置文件的描述,主要描述了該配置文件在classpath中的位置和可用於加載該配置文件的加載器。doLoadDocument方法比較簡單,主要是加載指定的配置文件,返回一個JDK內置的XML解析Document對象,以便於解析XML DOM節點。緩存
重點看下registerBeanDefinitions方法,以下:ide
該方法最終委託給了BeanDefinitonDocumentReader來完成Bean的解析。在這以前,初始化了一系列相關對象。包括:ui
初始相關對象後便將解析過程委託給了DefaultBeanDefinitionDocumentReader來進行處理,該類的重點爲parseBeanDefinitions方法,在調用該方法前,先初始化了一個BeanDefinitionParserDelegate對象,以下:spa
並將該delegate對象傳入了parseBeanDefinitions方法。BeanDefinitionParserDelegate主要提供了命名空間爲http://www.springframework.org/schema/beans
(下面簡寫爲beans空間)的XML文件解析過程。該命名空間定義了4個主要的XML標籤,分別爲beans
、bean
、import
和alias
以及這些標籤對應的屬性,以下爲該命名空間的示例,定義了一個基礎的Bean。3d
須要注意的是,上面在初始化BeanDefinitionParserDelegate後會先解析XML上<beans>
標籤上的默認屬性,包括:default-lazy-init
、default-merge
、default-autowire
、default-dependency-check
、default-autowire-candidates
、default-init-method
和default-destroy-method
這些全局屬性。代理
下面看下parseBeanBefinitoins方法:code
該方法會對當前節點所屬命名空間進行判斷,分爲默認命名空間和自定義命名空間,其中默認命名空間指的是上面提到的beans空間。對於默認命名空間,會逐一解析每一個DOM子節點,判斷子節點的命名空間,最終委託給兩個方法:處理默認命名空間的parseDefaultElement方法和處理自定義命名空間的parseClustomElement方法。
parseDefautlElement方法如上, 對beans空間定義的各個標籤分別進行了處理:
1) 獲取id屬性值做爲beanName
2) 獲取name屬性值做爲aliases,該屬性值能夠配置多個,以 , 或者 ; 符進行分割,將做爲該bean的別名使用;若id值爲空,且name不爲空,則使用第一個name值做爲id值
3) 檢查beanName和aliases的惟一性
4) 解析bean其餘配置,生成GenericBeanDefinition對象
5) 若beanName爲空,則爲其分配一個
對於步驟3.d
,處理過程爲:
1) 獲取class屬性值
2) 獲取parent屬性值
3) 初始化GenericBeanDefinition實例
4) 解析bean節點的屬性,設置到BeanDefinition中,包括:scope
、abstract
、lazy-init
、autowired
、dependency-check
、autowire-candidate
、primary
、init-method
、destroy-method
、factory-method
、factory-bean
5) 解析description子節點,獲取值設置bean的描述內容
6) 解析meta子節點列表,獲取key、value值設置附加元數據信息
7) 解析lookup-method子節點列表,獲取name、bean值設置方法注入信息
8) 解析replaced-method子節點列表,獲取值設置須要動態代理的方法信息
9) 解析constructor-arg子節點列表,獲取值設置構造參數信息
10) 解析property子節點列表,獲取值設置屬性信息
11) 解析qualifier子節點列表,獲取值進行設置
上面解析完bean的配置後,會再處理子節點中其餘命名空間的配置,使用NamespaceHandler的decorate方法,用以修改Bean定義內容,這部分將使用下面的內容,放在後面一塊兒講。
如上,默認命名空間主要用於解析bean的定義,通過上面的處理,bean定義的解析就已經完成,會將實例對象註冊到上下文中進行保存
下面介紹parseClustomElement方法,顧名思義,該方法主要用來處理自定義命名空間的XML標籤的,能夠當作是spring XML配置處理的一種擴展手段,以下,爲該方法的內容:
主要委託給了NamespaceHandlerResolver,經過查找到對應節點對應命名空間的Handler,調用該Handler的parse方法進行處理。
前面說過,NamespaceHandlerResolver使用了DefaultNamespaceHandlerResolver
做爲實現,跟蹤resolve方法進去以下:
過程爲:
對於第(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
屬性內容賦值給basePackage1) 解析設置use-default-filters參數
2) 解析設置resource-pattern參數
3) 解析設置name-generator參數
4) 解析設置scope-resolver、scoped-proxy等參數
5) 解析設置include-filter
、exclude-filter
等參數, ClassPathBeanDefinitionScanner對象再初始化時默認增長了org.springframework.stereotype. Component、javax.annotation.ManagedBean和javax.inject.Named幾種註解
這裏重點看下第(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方法,該方法添加了一系列用於後置解析的註解處理類定義到上下文中,包括:
@Configuration
功能,對應bean爲org.springframework.context.annotation.internalConfigurationAnnotationProcessor@Autowired
,@Value
功能,對應bean爲org.springframework.context.annotation.internalAutowiredAnnotationProcessor@Required
功能,對應bean爲org.springframework.context.annotation.internalRequiredAnnotationProcessor@PostConstruct
、@PreDestroy
功能,對應bean爲org.springframework.context.annotation.internalCommonAnnotationProcessor@PersistenceUnit
、@PersistenceContext
功能,對應bean爲org.springframework.context.annotation.internalPersistenceAnnotationProcessor@EventListener
功能,對應bean爲org.springframework.context.event.internalEventListenerProcessor以上各個bean在添加前都會先判斷是否已經存在改定義,若不存在才增長,於是能夠經過在添加相應bean的方式,修改對應的處理功能。
PS:第(6)步添加註解後置處理的方法其實也是annotation-config這個標籤功能的主要處理方法。由上可知annotation-config的處理類爲AnnotationConfigBeanDefinitionParser,該類內部其實也是調用了AnnotationConfigUtils的registerAnnotationConfigProcessors方法來完成註解的功能,具體代碼以下:
結合以前Spring啓動的內容,接上上面的內容,能夠獲得以下的接口回調順序
由於InstantiationAwareBeanPostProcessor、DestructionAwareBeanPostProcessor等接口繼承自MergedBeanDefinitionPostProcessor接口,MergedBeanDefinitionPostProcessor接口繼承自BeanPostrProcessor,實現類上存在重疊,這裏先根據講解順序排序。
我的公衆號:啊駝