SpringBoot啟動流程分析(三):SpringApplication的run方法之prepareContext()方法


SpringBoot系列文章簡介

SpringBoot源碼閱讀輔助篇:

  Spring IoC容器與應用上下文的設計與實現

SpringBoot啟動流程源碼分析:

  1. SpringBoot啟動流程分析(一):SpringApplication類初始化過程
  2. SpringBoot啟動流程分析(二):SpringApplication的run方法
  3. SpringBoot啟動流程分析(三):SpringApplication的run方法之prepareContext()方法
  4. SpringBoot啟動流程分析(四):IoC容器的初始化過程
  5. SpringBoot啟動流程分析(五):SpringBoot自動裝配原理實現
  6. SpringBoot啟動流程分析(六):IoC容器依賴注入

筆者注釋版Spring Framework與SpringBoot源碼git傳送門:請不要吝嗇小星星

  1. spring-framework-5.0.8.RELEASE
  2. SpringBoot-2.0.4.RELEASE

第四步:刷新應用上下文前的准備階段

一、prepareContext()方法

  前面我們介紹了SpringBoot 啟動流程run()方法的前三步,本章,我們將用一個章節介紹:第四步:刷新應用上下文前的准備階段。也就是prepareContext()方法。

  首先看prepareContext()方法。

 1 private void prepareContext(ConfigurableApplicationContext context,
 2                             ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
 3                             ApplicationArguments applicationArguments, Banner printedBanner) {
 4     //設置容器環境
 5     context.setEnvironment(environment);
 6     //執行容器后置處理
 7     postProcessApplicationContext(context);
 8     //執行容器中的 ApplicationContextInitializer 包括spring.factories和通過三種方式自定義的
 9     applyInitializers(context);
10     //向各個監聽器發送容器已經准備好的事件
11     listeners.contextPrepared(context);
12     if (this.logStartupInfo) {
13         logStartupInfo(context.getParent() == null);
14         logStartupProfileInfo(context);
15     }
16 
17     // Add boot specific singleton beans
18     //將main函數中的args參數封裝成單例Bean,注冊進容器
19     context.getBeanFactory().registerSingleton("springApplicationArguments",
20             applicationArguments);
21     //將 printedBanner 也封裝成單例,注冊進容器
22     if (printedBanner != null) {
23         context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
24     }
25 
26     // Load the sources
27     Set<Object> sources = getAllSources();
28     Assert.notEmpty(sources, "Sources must not be empty");
29     //加載我們的啟動類,將啟動類注入容器
30     load(context, sources.toArray(new Object[0]));
31     //發布容器已加載事件
32     listeners.contextLoaded(context);
33 }

  首先看這行 Set<Object> sources = getAllSources(); 在getAllSources()中拿到了我們的啟動類。  

  我們本文重點講解這行 load(context, sources.toArray(new Object[0])); ,其他的方法請參閱注釋。

  跟進load()方法,看源碼

 1 protected void load(ApplicationContext context, Object[] sources) {
 2     if (logger.isDebugEnabled()) {
 3         logger.debug(
 4                 "Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
 5     }
 6     //創建 BeanDefinitionLoader 
 7     BeanDefinitionLoader loader = createBeanDefinitionLoader(
 8             getBeanDefinitionRegistry(context), sources);
 9     if (this.beanNameGenerator != null) {
10         loader.setBeanNameGenerator(this.beanNameGenerator);
11     }
12     if (this.resourceLoader != null) {
13         loader.setResourceLoader(this.resourceLoader);
14     }
15     if (this.environment != null) {
16         loader.setEnvironment(this.environment);
17     }
18     loader.load();
19 }

 

1.1、getBeanDefinitionRegistry()

  繼續看getBeanDefinitionRegistry()方法的源碼

1 private BeanDefinitionRegistry getBeanDefinitionRegistry(ApplicationContext context) {
2     if (context instanceof BeanDefinitionRegistry) {
3         return (BeanDefinitionRegistry) context;
4     }
5     ...
6 }

  這里將我們前文創建的上下文強轉為BeanDefinitionRegistry,是不是很熟悉,前面的文章中咱們也介紹過,他們之間是有繼承關系的。BeanDefinitionRegistry定義了很重要的方法registerBeanDefinition(),該方法將BeanDefinition注冊進DefaultListableBeanFactory容器的beanDefinitionMap中。

 

1.2、createBeanDefinitionLoader()

  繼續看createBeanDefinitionLoader()方法,最終進入了BeanDefinitionLoader類的構造方法,如下

 1 BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
 2     Assert.notNull(registry, "Registry must not be null");
 3     Assert.notEmpty(sources, "Sources must not be empty");
 4     this.sources = sources;
 5     //注解形式的Bean定義讀取器 比如:@Configuration @Bean @Component @Controller @Service等等
 6     this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
 7     //XML形式的Bean定義讀取器
 8     this.xmlReader = new XmlBeanDefinitionReader(registry);
 9     if (isGroovyPresent()) {
10         this.groovyReader = new GroovyBeanDefinitionReader(registry);
11     }
12     //類路徑掃描器
13     this.scanner = new ClassPathBeanDefinitionScanner(registry);
14     //掃描器添加排除過濾器
15     this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
16 }

  先記住上面的三個屬性,具體有什么用,先看看注釋。前面的文章,我們說過,IoC容器的初始化分為三個步驟,上面三個屬性在,BeanDefinition的Resource定位,和BeanDefinition的注冊中起到了很重要的作用。

 

1.3、loader.load();

  跟進load()方法

 1 private int load(Object source) {
 2     Assert.notNull(source, "Source must not be null");
 3     // 從Class加載
 4     if (source instanceof Class<?>) {
 5         return load((Class<?>) source);
 6     }
 7     // 從Resource加載
 8     if (source instanceof Resource) {
 9         return load((Resource) source);
10     }
11     // 從Package加載
12     if (source instanceof Package) {
13         return load((Package) source);
14     }
15     // 從 CharSequence 加載 ???
16     if (source instanceof CharSequence) {
17         return load((CharSequence) source);
18     }
19     throw new IllegalArgumentException("Invalid source type " + source.getClass());
20 }

  當前我們的主類會按Class加載。

  繼續跟進load()方法。

 1 private int load(Class<?> source) {
 2     if (isGroovyPresent()
 3             && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
 4         // Any GroovyLoaders added in beans{} DSL can contribute beans here
 5         GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source,
 6                 GroovyBeanDefinitionSource.class);
 7         load(loader);
 8     }
 9     if (isComponent(source)) {
10         //將 啟動類的 BeanDefinition注冊進 beanDefinitionMap
11         this.annotatedReader.register(source);
12         return 1;
13     }
14     return 0;
15 }

  isComponent(source)判斷主類是不是存在@Component注解,主類@SpringBootApplication是一個組合注解(后面講解自動裝配會講解<SpringBoot啟動流程分析(五):SpringBoot自動裝配原理實現>),包含@Component。

  this.annotatedReader.register(source);跟進register()方法,最終進到AnnotatedBeanDefinitionReader類的doRegisterBean()方法。

 1 <T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
 2         @Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {
 3 
 4     //將指定的類 封裝為AnnotatedGenericBeanDefinition
 5     AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
 6     if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
 7         return;
 8     }
 9 
10     abd.setInstanceSupplier(instanceSupplier);
11     // 獲取該類的 scope 屬性
12     ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
13     abd.setScope(scopeMetadata.getScopeName());
14     String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
15 
16     AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
17     if (qualifiers != null) {
18         for (Class<? extends Annotation> qualifier : qualifiers) {
19             if (Primary.class == qualifier) {
20                 abd.setPrimary(true);
21             }
22             else if (Lazy.class == qualifier) {
23                 abd.setLazyInit(true);
24             }
25             else {
26                 abd.addQualifier(new AutowireCandidateQualifier(qualifier));
27             }
28         }
29     }
30     for (BeanDefinitionCustomizer customizer : definitionCustomizers) {
31         customizer.customize(abd);
32     }
33 
34     BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
35     definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
36     // 將該BeanDefinition注冊到IoC容器的beanDefinitionMap中
37     BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
38 }

  在該方法中將主類封裝成AnnotatedGenericBeanDefinition

  BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);方法將BeanDefinition注冊進beanDefinitionMap

 1 public static void registerBeanDefinition(
 2         BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
 3         throws BeanDefinitionStoreException {
 4     // Register bean definition under primary name.
 5     // primary name 其實就是id吧
 6     String beanName = definitionHolder.getBeanName();
 7     registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
 8     // Register aliases for bean name, if any.
 9     // 然后就是注冊別名
10     String[] aliases = definitionHolder.getAliases();
11     if (aliases != null) {
12         for (String alias : aliases) {
13             registry.registerAlias(beanName, alias);
14         }
15     }
16 }

   繼續跟進registerBeanDefinition()方法。

 1 @Override
 2 public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
 3         throws BeanDefinitionStoreException {
 4 
 5     Assert.hasText(beanName, "Bean name must not be empty");
 6     Assert.notNull(beanDefinition, "BeanDefinition must not be null");
 7 
 8     if (beanDefinition instanceof AbstractBeanDefinition) {
 9         try {
10             // 最后一次校驗了
11             // 對bean的Overrides進行校驗,還不知道會在哪處理這些overrides
12             ((AbstractBeanDefinition) beanDefinition).validate();
13         } catch (BeanDefinitionValidationException ex) {
14             throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
15                     "Validation of bean definition failed", ex);
16         }
17     }
18     // 判斷是否存在重復名字的bean,之后看允不允許override
19     // 以前使用synchronized實現互斥訪問,現在采用ConcurrentHashMap
20     BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
21     if (existingDefinition != null) {
22         //如果該類不允許 Overriding 直接拋出異常
23         if (!isAllowBeanDefinitionOverriding()) {
24             throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
25                     "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
26                             "': There is already [" + existingDefinition + "] bound.");
27         } else if (existingDefinition.getRole() < beanDefinition.getRole()) {
28             // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
29             if (logger.isWarnEnabled()) {
30                 logger.warn("Overriding user-defined bean definition for bean '" + beanName +
31                         "' with a framework-generated bean definition: replacing [" +
32                         existingDefinition + "] with [" + beanDefinition + "]");
33             }
34         } else if (!beanDefinition.equals(existingDefinition)) {
35             if (logger.isInfoEnabled()) {
36                 logger.info("Overriding bean definition for bean '" + beanName +
37                         "' with a different definition: replacing [" + existingDefinition +
38                         "] with [" + beanDefinition + "]");
39             }
40         } else {
41             if (logger.isDebugEnabled()) {
42                 logger.debug("Overriding bean definition for bean '" + beanName +
43                         "' with an equivalent definition: replacing [" + existingDefinition +
44                         "] with [" + beanDefinition + "]");
45             }
46         }
47         //注冊進beanDefinitionMap
48         this.beanDefinitionMap.put(beanName, beanDefinition);
49     } else {
50         if (hasBeanCreationStarted()) {
51             // Cannot modify startup-time collection elements anymore (for stable iteration)
52             synchronized (this.beanDefinitionMap) {
53                 this.beanDefinitionMap.put(beanName, beanDefinition);
54                 List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
55                 updatedDefinitions.addAll(this.beanDefinitionNames);
56                 updatedDefinitions.add(beanName);
57                 this.beanDefinitionNames = updatedDefinitions;
58                 if (this.manualSingletonNames.contains(beanName)) {
59                     Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
60                     updatedSingletons.remove(beanName);
61                     this.manualSingletonNames = updatedSingletons;
62                 }
63             }
64         } else {
65             // Still in startup registration phase
66             //如果仍處於啟動注冊階段,注冊進beanDefinitionMap
67             this.beanDefinitionMap.put(beanName, beanDefinition);
68             this.beanDefinitionNames.add(beanName);
69             this.manualSingletonNames.remove(beanName);
70         }
71         this.frozenBeanDefinitionNames = null;
72     }
73 
74     if (existingDefinition != null || containsSingleton(beanName)) {
75         resetBeanDefinition(beanName);
76     }
77 }

  最終來到DefaultListableBeanFactory類的registerBeanDefinition()方法,DefaultListableBeanFactory類還熟悉嗎?相信大家一定非常熟悉這個類了。DefaultListableBeanFactory是IoC容器的具體產品。

  仔細看這個方法registerBeanDefinition(),首先會檢查是否已經存在,如果存在並且不允許被覆蓋則直接拋出異常。不存在的話就直接注冊進beanDefinitionMap中。

  debug跳過prepareContext()方法,可以看到,啟動類的BeanDefinition已經注冊進來了。

   OK,到這里啟動流程的第五步就算講完了,其實在這沒必要講這么細,因為啟動類BeanDefinition的注冊流程和后面我們自定義的BeanDefinition的注冊流程是一樣的。這先介紹一遍這個流程,后面熟悉了這個流程就好理解了。后面馬上就到最最最重要的refresh()方法了。

 

  

  

  原創不易,轉載請注明出處。

  如有錯誤的地方還請留言指正。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM