SpringBoot系列文章簡介
SpringBoot源碼閱讀輔助篇:
SpringBoot啟動流程源碼分析:
- SpringBoot啟動流程分析(一):SpringApplication類初始化過程
- SpringBoot啟動流程分析(二):SpringApplication的run方法
- SpringBoot啟動流程分析(三):SpringApplication的run方法之prepareContext()方法
- SpringBoot啟動流程分析(四):IoC容器的初始化過程
- SpringBoot啟動流程分析(五):SpringBoot自動裝配原理實現
- SpringBoot啟動流程分析(六):IoC容器依賴注入
筆者注釋版Spring Framework與SpringBoot源碼git傳送門:請不要吝嗇小星星
第四步:刷新應用上下文前的准備階段
一、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()方法了。
原創不易,轉載請注明出處。
如有錯誤的地方還請留言指正。