以SOFABoot自帶的RPC案例sofaboot-sample-with-rpc作為SOFABoot應用(其實也是SpringBoot應用)啟動源碼分析的案例。
在此提前說明,源碼分析主要分析主流程,以及本人認為比較重要的一些内容,對于其它部分,大家可以基于本文檔,自行研讀。
在該案例中,采用WEB環境下使用的Spring應用上下文環境org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext。
在SofaBootRpcDemoApplication的main方法中,調用SpringApplication的靜态方法(S ofaBootRpcDemoApplication.class, args),啟動應用:
1. publicstatic ConfigurableApplicationContext run(Object[] sources, String[] args) {
2. return new SpringApplication(sources).run(args);
3. }
一、 建立SpringApplication執行個體
在run方法中,首先建立SpringApplication執行個體:
1. publicSpringApplication(Object... sources) {
2. initialize(sources);
3. }
在SpringApplication構造函數中,調用initialize方法進行SpringApplication執行個體初始化工作:
1. privatevoid initialize(Object[] sources) {
2. if(sources != null && sources.length > 0) {
3. this.sources.addAll(Arrays.asList(sources));
4. }
5. this.webEnvironment= deduceWebEnvironment();
6. setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
8. setListeners((Collection)getSpringFactoriesInstances(ApplicationListener.class));
9. this.mainApplicationClass= deduceMainApplicationClass();
10. }
在SpringApplication執行個體初始化的時候,主要做以下幾件事情:
1. 根據classpath裡面是否存在某個特征類(org.springframework.web.context.ConfigurableWebApplicationContext)來決定是否應該建立一個為Web應用使用的ApplicationContext類型;
2. 使用SpringFactoriesLoader在應用的classpath中查找并加載所有可用的ApplicationContextInitializer;
3. 使用SpringFactoriesLoader在應用的classpath中查找并加載所有可用的ApplicationListener;
4. 推斷并設定main方法的定義類;
二、 啟動SOFABoot應用
SpringApplication執行個體初始化完成後,開始執行run方法,啟動SOFABoot應用。
1. public ConfigurableApplicationContextrun(String... args) {
2. StopWatchstopWatch = new StopWatch();
3. stopWatch.start();
4. ConfigurableApplicationContextcontext = null;
5. FailureAnalyzersanalyzers = null;
6. configureHeadlessProperty();
7. SpringApplicationRunListenerslisteners = getRunListeners(args);
8. listeners.started();
9. try{
10. ApplicationArgumentsapplicationArguments = new DefaultApplicationArguments(args);
12. ConfigurableEnvironmentenvironment = prepareEnvironment(listeners,
13. applicationArguments);
14. BannerprintedBanner = printBanner(environment);
15. context = createApplicationContext();
16. analyzers= new FailureAnalyzers(context);
17. prepareContext(context, environment,listeners, applicationArguments,
18. printedBanner);
19. refreshContext(context);
20. afterRefresh(context,applicationArguments);
21. listeners.finished(context,null);
22. stopWatch.stop();
23. if(this.logStartupInfo) {
24. newStartupInfoLogger(this.mainApplicationClass)
25. .logStarted(getApplicationLog(),stopWatch);
26. }
27. returncontext;
28. }
29. catch(Throwable ex) {
30. handleRunFailure(context,listeners, analyzers, ex);
31. throw new IllegalStateException(ex);
32. }
33. }
啟動SOFABoot應用的主要工作就是建立和重新整理Spring應用上下文。此處采用AnnotationConfigEmbeddedWebApplicationContext作為Spring應用上下文的實作類。
啟動SOFABoot應用的主要步驟如下:
(一) 建立StopWatch執行個體,并啟動,記錄任務的運作時間;
(二) 設定系統屬性java.awt.headless;
(三) 通過SpringFactoriesLoader類loadFactoryNames方法,查找并加載所有spring.factories檔案中定義的SpringApplicationRunListener接口的實作,并調用started方法啟動監聽器;
(四) 建立Spring應用上下文AnnotationConfigEmbeddedWebApplicationContext: 1. protectedConfigurableApplicationContext createApplicationContext() {
2. Class<?>contextClass = this.applicationContextClass;
3. if(contextClass == null) {
4. try{
5. contextClass =Class.forName(this.webEnvironment
6. ?DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
7. }
8. catch(ClassNotFoundException ex) {
9. thrownew IllegalStateException(……略);
10. }
11. }
12. return (ConfigurableApplicationContext)BeanUtils.instantiate(contextClass);
13. }
此處是WEB環境,是以建立AnnotationConfigEmbeddedWebApplicationContext執行個體:
1. publicAnnotationConfigEmbeddedWebApplicationContext() {
2. this.reader = new AnnotatedBeanDefinitionReader(this);
3. this.scanner= new ClassPathBeanDefinitionScanner(this);
4. }
在AnnotationConfigEmbeddedWebApplicationContext構造函數中,建立AnnotatedBeanDefinitionReader執行個體,用來處理含有注解的類。建立ClassPathBeanDefinitionScanner執行個體,用來掃描指定路徑下含有注解的類。由于SOFAbBoot主要采用JavaConfig的配置形式,是以此處主要看一下AnnotatedBeanDefinitionReader執行個體化過程,以了解注解相關處理器的注冊過程:
1. publicAnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environmentenvironment) {
2. ……略
3. AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
4. }
在Spring應用上下文中注冊所有注解相關的Post Processors:
1. public staticSet<BeanDefinitionHolder> registerAnnotationConfigProcessors(
2. BeanDefinitionRegistryregistry, Object source) {
3.
4. DefaultListableBeanFactorybeanFactory = unwrapDefaultListableBeanFactory(registry);
5. ……略
6. Set<BeanDefinitionHolder>beanDefs = new LinkedHashSet<BeanDefinitionHolder>(4);
7. if(!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)){
8. RootBeanDefinitiondef = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
9. def.setSource(source);
10. beanDefs.add(registerPostProcessor(registry,def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
11. }
12.
13. if(!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
14. RootBeanDefinitiondef = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
15. def.setSource(source);
16. beanDefs.add(registerPostProcessor(registry,def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
17. }
18.
19. if(!registry.containsBeanDefinition(REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
20. RootBeanDefinitiondef = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class);
21. def.setSource(source);
22. beanDefs.add(registerPostProcessor(registry,def, REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
23. }
24.
25. //Check for JSR-250 support, and if present add theCommonAnnotationBeanPostProcessor.
26. if(jsr250Present &&!registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
27. RootBeanDefinitiondef = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
28. def.setSource(source);
29. beanDefs.add(registerPostProcessor(registry,def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
30. }
31. ……略
32. if(!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
33. RootBeanDefinition def = newRootBeanDefinition(EventListenerMethodProcessor.class);
34. def.setSource(source);
35. beanDefs.add(registerPostProcessor(registry,def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
36. }
37. if(!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
38. RootBeanDefinitiondef = new RootBeanDefinition(DefaultEventListenerFactory.class);
39. def.setSource(source);
40. beanDefs.add(registerPostProcessor(registry,def, EVENT_LISTENER_FACTORY_BEAN_NAME));
41. }
42.
43. returnbeanDefs;
44. }
與注解相關的PostProcessors主要包括ConfigurationClassPostProcessor,AutowiredAnnotationBeanPostProcessor,RequiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor,EventListenerMethodProcessor,PersistenceAnnotationBeanPostProcessor,DefaultEventListenerFactory。
其中,ConfigurationClassPostProcessor實作了BeanFactoryPostProcessor接口。當重新整理Spring上下文(調用AbstractApplicationContext類refresh方法)過程中,調用AbstractApplicationContext類invokeBeanFactoryPostProcessors方法時,被調用執行。ConfigurationClassPostProcessor解析@Configuration注解,調用ConfigurationClassParser類的parse方法,處理@ComponentScan、@Import、@ImportResource、@Bean注解。
AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor, RequiredAnnotationBeanPostProcessor,PersistenceAnnotationBeanPostProcessor實作了BeanPostProcessor接口,在建立Bean執行個體時被調用。例如:在重新整理Spring上下文(調用AbstractApplicationContext類refresh方法)過程中,調用finishBeanFactoryInitialization方法時,在執行個體化所有單例Bean對象時被調用。
EventListenerMethodProcessor,DefaultEventListenerFactory是事件監聽器相關的PostProcessors。
至此,建立AnnotationConfigEmbeddedWebApplicationContext執行個體的主要步驟完成。
(五) 準備Spring應用上下文: 1. private voidprepareContext(ConfigurableApplicationContext context,
2. ConfigurableEnvironmentenvironment, SpringApplicationRunListeners listeners,
3. ApplicationArgumentsapplicationArguments, Banner printedBanner) {
4. context.setEnvironment(environment);
5. postProcessApplicationContext(context);
6. applyInitializers(context);
7. listeners.contextPrepared(context);
8. if(this.logStartupInfo) {
9. logStartupInfo(context.getParent()== null);
10. logStartupProfileInfo(context);
11. }
12.
13. //Add boot specific singleton beans
14. context.getBeanFactory().registerSingleton("springApplicationArguments",
15. applicationArguments);
16. if(printedBanner != null) {
17. context.getBeanFactory().registerSingleton("springBootBanner",printedBanner);
18. }
19.
20. //Load the sources
21. Set<Object>sources = getSources();
22. Assert.notEmpty(sources,"Sources must not be empty");
23. load(context, sources.toArray(newObject[sources.size()]));
24. listeners.contextLoaded(context);
25. }
1. 設定Spring應用上下文Enviroment;
2. 設定Spring應用上下文屬性,如beanNameGenerator、resourceLoader等;
3. 依次調用通過SpringFactoriesLoader從spring.factories檔案中加載的ApplicationContextInitializer接口的實作的initialize方法,完成Spring應用上下文的初始化工作;
4. 依次調用通過SpringFactoriesLoader從spring.factories檔案中加載的SpringApplicationRunListener接口的實作的contextPrepared方法。該方法在Spring應用上下文建立完,并且前期準備工作完成,但加載資源以前,完成Spring應用上下文的一些處理工作;
5. 建立BeanDefinitionLoader執行個體(構造函數中依次建立AnnotatedBeanDefinitionReader、XmlBeanDefinitionReader、ClassPathBeanDefinitionScanner),然後調用該執行個體的load方法注冊run方法中參數指定的source資源(此處為SofaBootRpcDemoApplication)到Spring應用上下文,這個資源是ConfigurationClassPostProcessor類處理的入口配置類,可以是一個或多個;
6. 依次調用通過SpringFactoriesLoader從spring.factories檔案中加載的SpringApplicationRunListener接口的實作的contextLoaded方法。該方法在Spring應用上下文已經被加載,但還沒有重新整理之前,完成Spring應用上下文的一些處理工作。
(六) 重新整理Spring應用上下文: 1. public void refresh() throws BeansException,IllegalStateException {
2. synchronized(this.startupShutdownMonitor) {
3. //Prepare this context for refreshing.
4. prepareRefresh();
5.
6. //Tell the subclass to refresh the internal bean factory.
7. ConfigurableListableBeanFactorybeanFactory = obtainFreshBeanFactory();
8.
9. //Prepare the bean factory for use in this context.
10. prepareBeanFactory(beanFactory);
11.
12. try{
13. //Allows post-processing of the bean factory in context subclasses.
14. postProcessBeanFactory(beanFactory);
15.
16. //Invoke factory processors registered as beans in the context.
17. invokeBeanFactoryPostProcessors(beanFactory);
18.
19. //Register bean processors that intercept bean creation.
20. registerBeanPostProcessors(beanFactory);
21.
22. //Initialize message source for this context.
23. initMessageSource();
24.
25. //Initialize event multicaster for this context.
26. initApplicationEventMulticaster();
27.
28. //Initialize other special beans in specific context subclasses.
29. onRefresh();
30.
31. //Check for listener beans and register them.
32. registerListeners();
33.
34. //Instantiate all remaining (non-lazy-init) singletons.
35. finishBeanFactoryInitialization(beanFactory);
36.
37. //Last step: publish corresponding event.
38. finishRefresh();
39. }
40.
41. catch(BeansException ex) {
42. if(logger.isWarnEnabled()) {
43. logger.warn("Exceptionencountered during context initialization - " +
44. "cancellingrefresh attempt: " + ex);
45. }
46.
47. //Destroy already created singletons to avoid dangling resources.
48. destroyBeans();
49.
50. //Reset 'active' flag.
51. cancelRefresh(ex);
52.
53. //Propagate exception to caller.
54. throwex;
55. }
56.
57. finally{
58. //Reset common introspection caches in Spring's core, since we
59. //might not ever need metadata for singleton beans anymore...
60. resetCommonCaches();
61. }
62. }
63. }
看到這段代碼,Spring開發人員都應該比較熟悉了。這段代碼為Spring應用上下文的具體實作類調用父類AbstractApplicationContext的refresh方法進行重新整理操作。對于整個Spring應用上下文重新整理過程,大家應該都比較熟悉了,在此不詳述了。
此時,我們主要關注invokeBeanFactoryPostProcessors(beanFactory)方法: 1. protected voidinvokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
2. PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory,getBeanFactoryPostProcessors());
3.
4. ……略
5. }
該方法通過PostProcessorRegistrationDelegate調用所有實作BeanFactoryPostProcessor接口的實作類,完成beanFactory的一些處理工作。
1. public static voidinvokeBeanFactoryPostProcessors(
2. ConfigurableListableBeanFactorybeanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
3.
4. ……略
5.
6. //First, invoke the BeanDefinitionRegistryPostProcessors that implementPriorityOrdered.
7. ……略
8. invokeBeanDefinitionRegistryPostProcessors(priorityOrderedPostProcessors,registry);
9.
10. //Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
11. ……略
12. invokeBeanDefinitionRegistryPostProcessors(orderedPostProcessors,registry);
13.
14. //Finally, invoke all other BeanDefinitionRegistryPostProcessors until no furtherones appear.
15. ……略
16.
17. //Now, invoke the postProcessBeanFactory callback of all processors handled sofar.
18. invokeBeanFactoryPostProcessors(registryPostProcessors,beanFactory);
19. invokeBeanFactoryPostProcessors(regularPostProcessors,beanFactory);
20. }
21.
22. else{
23. //Invoke factory processors registered with the context instance.
24. invokeBeanFactoryPostProcessors(beanFactoryPostProcessors,beanFactory);
25. }
26.
27. ……略
28. //Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
29. //Ordered, and the rest.
30. ……略
31. //First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
32. ……略
33. invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors,beanFactory);
34.
35. //Next, invoke the BeanFactoryPostProcessors that implement Ordered.
36. ……略
37. invokeBeanFactoryPostProcessors(orderedPostProcessors,beanFactory);
38.
39. //Finally, invoke all other BeanFactoryPostProcessors.
40. ……略
41. invokeBeanFactoryPostProcessors(nonOrderedPostProcessors,beanFactory);
42.
43. ……略
44. } 在invokeBeanDefinitionRegistryPostProcessors方法中,調用實作了PriorityOrdered接口和BeanDefinitionRegistryPostProcessor接口的BeanFactoryPostProcessor的實作類ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法: 1. public voidpostProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
2.
3. RootBeanDefinition iabpp = newRootBeanDefinition(ImportAwareBeanPostProcessor.class);
4. iabpp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
5. registry.registerBeanDefinition(IMPORT_AWARE_PROCESSOR_BEAN_NAME,iabpp);
6.
7. RootBeanDefinitionecbpp = new RootBeanDefinition(EnhancedConfigurationBeanPostProcessor.class);
8. ecbpp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
9. registry.registerBeanDefinition(ENHANCED_CONFIGURATION_PROCESSOR_BEAN_NAME,ecbpp);
10. ……略
11.
12. processConfigBeanDefinitions(registry);
13. }
在Spring應用上下文中注冊BeanPostProcessor接口的實作類ImportAwareBeanPostProcessor、EnhancedConfigurationBeanPostProcessor;
調用processConfigBeanDefinitions方法,開始處理@Configuration配置類。
注意 ,此處是整個應用中所有@Configuration配置類的處理入口。在整個@Configuration配置類處理過程中,循環處理所有@Configuration配置類,直至所有@Configuration配置類處理完。 1. public void processConfigBeanDefinitions(BeanDefinitionRegistryregistry) {
2. List<BeanDefinitionHolder>configCandidates = new ArrayList<BeanDefinitionHolder>();
3. String[]candidateNames = registry.getBeanDefinitionNames();
4.
5. for(String beanName : candidateNames) {
6. BeanDefinitionbeanDef = registry.getBeanDefinition(beanName);
7. if(ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
8. ConfigurationClassUtils.isLiteConfigurationClass(beanDef)){
9. ……略
10. }
11. elseif (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef,this.metadataReaderFactory)) {
12. configCandidates.add(newBeanDefinitionHolder(beanDef, beanName));
13. }
14. }
15. ……略
16. ConfigurationClassParser parser = new ConfigurationClassParser(
17. this.metadataReaderFactory,this.problemReporter, this.environment,
18. this.resourceLoader,this.componentScanBeanNameGenerator, registry);
19. ……略
20. do {
21. parser.parse(candidates);
22. parser.validate();
23.
24. ……略
25. // Read the model and create beandefinitions based on its content
26. if(this.reader == null) {
27. this.reader= new ConfigurationClassBeanDefinitionReader(
28. registry,this.sourceExtractor, this.resourceLoader, this.environment,
29. this.importBeanNameGenerator,parser.getImportRegistry());
30. }
31. this.reader.loadBeanDefinitions(configClasses);
32. alreadyParsed.addAll(configClasses);
33.
34. candidates.clear();
35. ……略
36. for (ConfigurationClassconfigurationClass : alreadyParsed) {
37. alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
38. }
39. for(String candidateName : newCandidateNames) {
40. if(!oldCandidateNames.contains(candidateName)) {
41. BeanDefinitionbeanDef = registry.getBeanDefinition(candidateName);
42. if(ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef,this.metadataReaderFactory) &&
43. !alreadyParsedClasses.contains(beanDef.getBeanClassName())){
44. candidates.add(newBeanDefinitionHolder(beanDef, candidateName));
45. }
46. }
47. }
48. }
49. while (!candidates.isEmpty());
50.
51. ……略
52. }
@Configuration配置類主要處理步驟:
1. 依次周遊DefaultListableBeanFactory中所有BeanDefinition,查找帶有@Configuration注解的BeanDefinition,放入configCandidates。然後根據@order進行排序,此時隻有一個包含@Configuration注解的類,即SofaBootRpcDemoApplication;
2. 構造@Configuration注解的解析類ConfigurationClassParser;
3. 依次周遊集合candidates中所有@Configuration配置類,逐一處理配置類中的注解,此時隻有SofaBootRpcDemoApplication。 1. public voidparse(Set<BeanDefinitionHolder> configCandidates) {
2.
3. this.deferredImportSelectors = newLinkedList<DeferredImportSelectorHolder>();
4.
5. for (BeanDefinitionHolder holder :configCandidates) {
6. BeanDefinitionbd = holder.getBeanDefinition();
7. try{
8. if(bd instanceof AnnotatedBeanDefinition) {
9. parse(((AnnotatedBeanDefinition)bd).getMetadata(), holder.getBeanName());
10. }
11. elseif (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition)bd).hasBeanClass()) {
12. parse(((AbstractBeanDefinition)bd).getBeanClass(), holder.getBeanName());
13. }
14. else{
15. parse(bd.getBeanClassName(),holder.getBeanName());
16. }
17. }
18. catch(BeanDefinitionStoreException ex) {
19. throwex;
20. }
21. catch(Throwable ex) {
22. thrownew BeanDefinitionStoreException(……略);
23. }
24. }
25. processDeferredImportSelectors();
26. }
依次處理每個@Configuration配置類,具體處理操作在doProcessConfigurationClass方法中實作,由于該方法負責具體處理每個@Configuration配置類,稍後在後面具體介紹。
處理完傳入的@Configuration配置類以後,處理實作DeferredImportSelector接口的ImportSelector類,主要用于有條件的加載某些類,如使用@Conditional注解。 1. private voidprocessDeferredImportSelectors() {
2. List<DeferredImportSelectorHolder>deferredImports = this.deferredImportSelectors;
3. ……略
4. for(DeferredImportSelectorHolder deferredImport : deferredImports) {
5. ConfigurationClassconfigClass = deferredImport.getConfigurationClass();
6. try{
7. String[] imports =deferredImport.getImportSelector().selectImports(configClass.getMetadata());
8. processImports(configClass,asSourceClass(configClass), asSourceClasses(imports), false);
9. }
10. catch(BeanDefinitionStoreException ex) {
11. throwex;
12. }
13. catch(Throwable ex) {
14. thrownew BeanDefinitionStoreException(……略);
15. }
16. }
17. }
依次處理所有實作DeferredImportSelector接口的ImportSelector類。
第一次循環時,隻有一個類,即由SofaBootRpcDemoApplication類導入的EnableAutoConfigurationImportSelector類,調用其selectImports方法,解析出所有支援自動配置的@Configuration配置類:
1. public String[]selectImports(AnnotationMetadata metadata) {
2. if(!isEnabled(metadata)) {
3. returnNO_IMPORTS;
4. }
5. try{
6. AnnotationAttributesattributes = getAttributes(metadata);
7. List<String> configurations =getCandidateConfigurations(metadata,
8. attributes);
9. configurations= removeDuplicates(configurations);
10. Set<String>exclusions = getExclusions(metadata, attributes);
11. configurations.removeAll(exclusions);
12. configurations= sort(configurations);
13. recordWithConditionEvaluationReport(configurations,exclusions);
14. returnconfigurations.toArray(new String[configurations.size()]);
15. }
16. catch(IOException ex) {
17. thrownew IllegalStateException(ex);
18. }
19. } 調用getCandidateConfigurations方法,擷取所有@Configuration配置類的名字: 1. protected List<String>getCandidateConfigurations(AnnotationMetadata metadata,
2. AnnotationAttributesattributes) {
3. List<String> configurations =SpringFactoriesLoader.loadFactoryNames(
4. getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader());
5. ……略
6. return configurations;
7. } 通過SpringFactoriesLoader的loadFactoryNames方法,從所有的spring.fatories檔案中,加載key為org.springframework.boot.autoconfigure.EnableAutoConfiguration的所有@Configuration配置類的名字。 1. public static List<String>loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
2. StringfactoryClassName = factoryClass.getName();
3. try{
4. Enumeration<URL> urls = (classLoader !=null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
5. ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
6. List<String>result = new ArrayList<String>();
7. while (urls.hasMoreElements()) {
8. URLurl = urls.nextElement();
9. Propertiesproperties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
10. StringfactoryClassNames = properties.getProperty(factoryClassName);
11. result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
12. }
13. returnresult;
14. }
15. catch(IOException ex) {
16. thrownew IllegalArgumentException(……略);
17. }
18. } 擷取所有@Configuration配置類的名字以後,進行去重、排序、篩選等操作。然後調用processImports方法,處理通過EnableAutoConfigurationImportSelector類導入的@Configuration配置類: 1. private voidprocessImports(ConfigurationClass configClass, SourceClass currentSourceClass,
2. Collection<SourceClass>importCandidates, boolean checkForCircularImports) throws IOException {
3.
4. ……略
5. if(checkForCircularImports && isChainedImportOnStack(configClass)) {
6. ……略
7. }
8. else{
9. this.importStack.push(configClass);
10. try{
11. for (SourceClass candidate : importCandidates){
12. if(candidate.isAssignable(ImportSelector.class)) {
13. //Candidate class is an ImportSelector -> delegate to it to determine imports
14. Class<?>candidateClass = candidate.loadClass();
15. ImportSelectorselector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
16. ParserStrategyUtils.invokeAwareMethods(
17. selector,this.environment, this.resourceLoader, this.registry);
18. if(this.deferredImportSelectors != null && selector instanceofDeferredImportSelector) {
19. this.deferredImportSelectors.add(
20. newDeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
21. }
22. else{
23. String[]importClassNames = selector.selectImports(currentSourceClass.getMetadata());
24. Collection<SourceClass>importSourceClasses = asSourceClasses(importClassNames);
25. processImports(configClass,currentSourceClass, importSourceClasses, false);
26. }
27. }
28. elseif (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
29. //Candidate class is an ImportBeanDefinitionRegistrar ->
30. //delegate to it to register additional bean definitions
31. Class<?>candidateClass = candidate.loadClass();
32. ImportBeanDefinitionRegistrarregistrar =
33. BeanUtils.instantiateClass(candidateClass,ImportBeanDefinitionRegistrar.class);
34. ParserStrategyUtils.invokeAwareMethods(
35. registrar,this.environment, this.resourceLoader, this.registry);
36. configClass.addImportBeanDefinitionRegistrar(registrar,currentSourceClass.getMetadata());
37. }
38. else{
39. //Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
40. //process it as an @Configuration class
41. this.importStack.registerImport(
42. currentSourceClass.getMetadata(),candidate.getMetadata().getClassName());
43. processConfigurationClass(candidate.asConfigClass(configClass));
44. }
45. }
46. }
47. catch(BeanDefinitionStoreException ex) {
48. throwex;
49. }
50. catch(Throwable ex) {
51. ……略
52. }
53. finally{
54. this.importStack.pop();
55. }
56. }
57. }
processImports方法主要邏輯:
1) 如果導入的配置類實作了DeferredImportSelector接口,則建立DeferredImportSelectorHolder執行個體,并置入deferredImportSelectors連結清單中,待上述processConfigBeanDefinitions方法下一個do{*}while(*)循環中處理。
2) 如果導入的配置類實作了ImportSelector接口,則調用該類的selectImports方法導入新的@Configuration配置類,并嵌套調用processImports方法,處理導入的@Configuration配置類。
3) 如果導入的配置類實作了ImportBeanDefinitionRegistrar接口,則建立ImportBeanDefinitionRegistrar執行個體,并在處理@Configuration配置類過程中,委托該執行個體注冊額外的BeanDefinition。
4) 如果以上都不是,則導入的配置類為基本的@Configuration配置類,直接調用processConfigurationClass處理,最終具體解析操作在doProcessConfigurationClass方法中實作。
1. protected final SourceClassdoProcessConfigurationClass(ConfigurationClass configClass, SourceClasssourceClass) throws IOException {
2. //Recursively process any member (nested) classes first
3. processMemberClasses(configClass,sourceClass);
4.
5. //處理@PropertySource注解
6. for(AnnotationAttributes propertySource :AnnotationConfigUtils.attributesForRepeatable(
7. sourceClass.getMetadata(),PropertySources.class, org.springframework.context.annotation.PropertySource.class)){
8. if(this.environment instanceof ConfigurableEnvironment) {
9. processPropertySource(propertySource);
10. }
11. else{
12. logger.warn("[email protected] annotation on [" + sourceClass.getMetadata().getClassName()+
13. "].Reason: Environment must implement ConfigurableEnvironment");
14. }
15. }
16.
17. // 處理@ComponentScan注解
18. Set<AnnotationAttributes>componentScans = AnnotationConfigUtils.attributesForRepeatable(
19. sourceClass.getMetadata(),ComponentScans.class, ComponentScan.class);
20. if(!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(),ConfigurationPhase.REGISTER_BEAN)) {
21. for(AnnotationAttributes componentScan : componentScans) {
22. //The config class is annotated with @ComponentScan -> perform the scanimmediately
23. Set<BeanDefinitionHolder>scannedBeanDefinitions =
24. this.componentScanParser.parse(componentScan,sourceClass.getMetadata().getClassName());
25. //Check the set of scanned definitions for any further config classes and parserecursively if necessary
26. for(BeanDefinitionHolder holder : scannedBeanDefinitions) {
27. if(ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(),this.metadataReaderFactory)) {
28. parse(holder.getBeanDefinition().getBeanClassName(),holder.getBeanName());
29. }
30. }
31. }
32. }
33.
34. //處理@Import注解
35. processImports(configClass, sourceClass,getImports(sourceClass), true);
36.
37. //處理@ImportResource注解
38. if(sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
39. AnnotationAttributesimportResource =
40. AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(),ImportResource.class);
41. String[]resources = importResource.getStringArray("locations");
42. Class<?extends BeanDefinitionReader> readerClass =importResource.getClass("reader");
43. for(String resource : resources) {
44. StringresolvedResource = this.environment.resolveRequiredPlaceholders(resource);
45. configClass.addImportedResource(resolvedResource,readerClass);
46. }
47. }
48.
49. //處理@Bean方法
50. Set<MethodMetadata>beanMethods =sourceClass.getMetadata().getAnnotatedMethods(Bean.class.getName());
51. for(MethodMetadata methodMetadata : beanMethods) {
52. configClass.addBeanMethod(newBeanMethod(methodMetadata, configClass));
53. }
54.
55. ……略
56.
57. //No superclass -> processing is complete
58. returnnull;
59. }
針對每個@Configuration配置類,依次處理@Configuration配置類中的@PropertySource、@ComponentScan、@Import、@ImportResource、@Bean注解,主要解析過程如下:
1) @PropertySource處理過程:調用processPropertySource方法處理屬性源,在此不詳述;
2) @ComponentScan處理過程:解析@Configuration配置類中@ComponentScan、@ComponentScans注解,并根據設定的basePackages掃描指定的包路徑,并加載、注冊所有帶有@Component注解及@Component衍生注解(如@Controller、@Service、@Repository、@Configuration等)Java類的Bean定義到Spring應用上下文;
3) @Import處理過程:參考上述processImports方法描述。需要注意,在處理某個配置類的@Import注解過程中,如果發現@Import導入的類實作了DeferredImportSelector接口,則建立DeferredImportSelectorHolder執行個體,并置入deferredImportSelectors連結清單中,待下一次循環處理。以此類推,直至所有的@Configuration配置類加載完畢。例如:在處理SofaBootRpcDemoApplication的@Import注解時,由于在SofaBootRpcDemoApplication類@SpringBootApplication注解中引用了@EnableAutoConfiguration注解,而它又引用了@Import(value={EnableAutoConfigurationImportSelector.class})。由于EnableAutoConfigurationImportSelector實作了DeferredImportSelector接口,是以在此建立DeferredImportSelectorHolder執行個體,并置入deferredImportSelectors連結清單中,待所有@Configuration注解類解析完成以後再處理。
4) @ImportResource處理過程:解析locations屬性,并依次擷取locations指定目錄下Spring XML配置檔案,設定configClass類的importedResource屬性;
5) @Bean處理過程:讀取@Configuration配置類中@Bean注解的方法,并增加到configClass的beanMethod集合。
至此,處理完現有candidates中所有@Configuration配置類。
在回看一下processConfigBeanDefinitions方法: 1. public voidprocessConfigBeanDefinitions(BeanDefinitionRegistry registry) {
2. List<BeanDefinitionHolder>configCandidates = new ArrayList<BeanDefinitionHolder>();
3. String[]candidateNames = registry.getBeanDefinitionNames();
4.
5. for(String beanName : candidateNames) {
6. BeanDefinitionbeanDef = registry.getBeanDefinition(beanName);
7. if(ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
8. ConfigurationClassUtils.isLiteConfigurationClass(beanDef)){
9. ……略
10. }
11. elseif (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef,this.metadataReaderFactory)) {
12. configCandidates.add(newBeanDefinitionHolder(beanDef, beanName));
13. }
14. }
15. ……略
16. ConfigurationClassParser parser = newConfigurationClassParser(
17. this.metadataReaderFactory,this.problemReporter, this.environment,
18. this.resourceLoader,this.componentScanBeanNameGenerator, registry);
19. ……略
20. do {
21. parser.parse(candidates);
22. parser.validate();
23.
24. ……略
25. // Read the model and create beandefinitions based on its content
26. if(this.reader == null) {
27. this.reader= new ConfigurationClassBeanDefinitionReader(
28. registry,this.sourceExtractor, this.resourceLoader, this.environment,
29. this.importBeanNameGenerator,parser.getImportRegistry());
30. }
31. this.reader.loadBeanDefinitions(configClasses);
32.
33. alreadyParsed.addAll(configClasses);
34. candidates.clear();
35. ……略
36. for (ConfigurationClassconfigurationClass : alreadyParsed) {
37. alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
38. }
39. for(String candidateName : newCandidateNames) {
40. if(!oldCandidateNames.contains(candidateName)) {
41. BeanDefinitionbeanDef = registry.getBeanDefinition(candidateName);
42. if(ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef,this.metadataReaderFactory) &&
43. !alreadyParsedClasses.contains(beanDef.getBeanClassName())){
44. candidates.add(newBeanDefinitionHolder(beanDef, candidateName));
45. }
46. }
47. }
48. }
49. while (!candidates.isEmpty());
50.
51. ……略
52. }
處理完現有candidates中所有@Configuration配置類以後,調用ConfigurationClassBeanDefinitionReader的loadBeanDefinitions方法加載、注冊@Configuration配置類本身及其中的@Bean注解對應的類。
1. public voidloadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
2. TrackedConditionEvaluatortrackedConditionEvaluator = new TrackedConditionEvaluator();
3. for(ConfigurationClass configClass : configurationModel) {
4. loadBeanDefinitionsForConfigurationClass(configClass,trackedConditionEvaluator);
5. }
6. } @Configuration配置類的具體加載方法如下: 1. private void loadBeanDefinitionsForConfigurationClass(ConfigurationClassconfigClass,
2. TrackedConditionEvaluatortrackedConditionEvaluator) {
3.
4. ……略
5.
6. if(configClass.isImported()) {
7. registerBeanDefinitionForImportedConfigurationClass(configClass);
8. }
9. for(BeanMethod beanMethod : configClass.getBeanMethods()) {
10. loadBeanDefinitionsForBeanMethod(beanMethod);
11. }
12. loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
13. loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
14. }
@Configuration配置類主要加載過程如下:
1) 把@Configuration配置類自身注冊到Spring應用上下文;
2) 加載@Bean注解對應的BeanDefinition;
3) 加載@ImportResource注解指定的XML檔案或Groovy檔案中配置的BeanDefinition;
4) 從ImportBeanDefinitionRegistrar加載BeanDefinition,如AutoConfigurationPackages;
在此就不詳述注冊過程,感興趣的同學可以自己看看。
接下來先清空candidates集合,然後從Spring應用上下文中擷取所有已經注冊的BeanDefinitionNames,然後與已經解析的@Configuration配置類對比,查找未處理的@Configuration配置類,并置入candidates集合,待下一次循環處理。此處循環處理下去,直至所有的@Configuration配置類處理完畢。
至此,ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法執行完畢,加載完所有配置的BeanDefinition。
不在詳述AbstractApplicationContext的refresh方法中其它步驟,感興趣的同學可以自己看看。
(七) Spring應用上下文重新整理以後的處理:
依次調用ApplicationRunner接口和CommandLineRunner接口的實作,完成重新整理Spring上下文以後的一些處理操作。
(八) 終止監聽
依次調用通過SpringFactoriesLoader從spring.factories檔案中加載的SpringApplicationRunListener接口的實作的finished方法,結束監聽器。
(九) 結束run方法
調用StopWatch執行個體的stop方法,結束記錄任務的運作時間。
到此為止,SOFABoot應用啟動完成。
三、總結
通過以上源碼分析,可以總結出以下幾個部分,作為基于SOFABoot架構進行開發時的基礎和擴充的方向。
(一) EnableAutoConfiguration
基于SOFABoot架構開發的子產品,通過在META-INF/spring.factories檔案中配置EnableAutoConfiguration實作SOFABoot子產品自動配置功能。
在SOFABoot應用啟動時,自動加載SOFABoot子產品META-INF/spring.factories檔案中配置的@Configuration配置類,以此作為子產品加載的入口,把子產品中相關的Bean加載到Spring應用上下文。
參考配置格式為:
1. # Auto Configure
2. org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
3. org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
4. org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
5. org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
6. org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration,\
7. org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration,\
8. org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
9. org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
10. org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
11. org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
這些支援自動配置的Bean在ApplicationContext重新整理過程中被注冊到Spring應用上下文,具體加載過程參考源碼解析部分。
(二) ApplicationContextInitializer
如果希望對Spring應用上下文進行一些個性化的初始化操作,則實作ApplicationContextInitializer接口,在initialize方法中完成個性化的初始化操作。
在META-INF/spring.factories檔案配置ApplicationContextInitializer接口的實作,格式為:
1. org.springframework.context.ApplicationContextInitializer=\
2. xxx.yyyy.zzz.XxxInitializer,\
3. xxx.yyyy.zzz.YyyInitializer
這些ApplicationContextInitializer接口的實作類在Spring應用上下文準備階段被調用執行,即SpringApplication的prepareContext方法中。
(三) ApplicationListener
如果希望監聽Spring應用的事件,當事件發生時,針對事件進行一些個性化操作,則實作ApplicationListener接口,在onApplicationEvent方法中處理發生的Application事件。
在META-INF/spring.factories檔案配置ApplicationListener接口的實作,格式為:
1. org.springframework.context.ApplicationListener=\
2. xxx.yyyy.zzz.XxxListener
當ApplicationEvent事件發生時,這些ApplicationListener接口的實作被調用。
(四) SpringApplicationRunListener
如果希望監聽SpringBoot的org.springframework.boot.SpringApplication類的run方法,當SpringApplication啟動時,進行一些個性化操作,則實作SpringApplicationRunListener接口,針對具體情況實作相應的方法。
1. public interfaceSpringApplicationRunListener {
2.
3.
6. void started();
7.
8.
11. void environmentPrepared(ConfigurableEnvironmentenvironment);
12.
13.
16. voidcontextPrepared(ConfigurableApplicationContext context);
17.
18.
21. voidcontextLoaded(ConfigurableApplicationContext context);
22.
23.
26. void finished(ConfigurableApplicationContextcontext, Throwable exception);
27.
28. }
在SpringApplication的run方法中,通過SpringFactoriesLoader類loadFactoryNames方法,查找并加載所有spring.factories檔案中定義的SpringApplicationRunListener接口的實作。
SpringApplicationRunListener接口中各個方法的調用順序為:
1. 在SpringApplication的run方法中,調用started方法;
2. 在SpringApplication的prepareEnvironment方法中,調用environmentPrepared方法;
3. 在SpringApplication的prepareContext方法中,先調用contextPrepared方法,然後是Spring上下文資源加載,最後調用contextLoaded方法;
4. 在SpringApplication的run方法中,調用finished方法。