通常狀況下啓動SpringBoot都是新建一個類包含main
方法,而後使用SpringApplication.run
來啓動程序:java
@SpringBootApplication public class AutoConfigApplication { public static void main(String[] args){ ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(AutoConfigApplication.class,args); } }
SpringApplication.run
接收兩個參數分別爲:primarySource、運行參數(args),上面的代碼使用AutoConfigApplication.class
做爲primarySource。SpringApplication還有一個實例方法也叫run
,SpringBoot的大部分啓動都由實例run
方法來完成的,其中構造ApplicationContext由createApplicationContext
方法完成:web
protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { switch (this.webApplicationType) { case SERVLET: contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); break; case REACTIVE: contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); break; default: contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); } } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex); } } return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); }
createApplicationContext
根據this.webApplicationType
來構造ApplicationContext,不一樣的環境都會使用不一樣的實例,但本文非web環境全部構造的時候會使用AnnotationConfigApplicationContext
類。建立AnnotationConfigApplicationContext
的時候會調用默認構造方法
:spring
public AnnotationConfigApplicationContext() { this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); }
AnnotationConfigApplicationContext默認構造全建立兩個對象:app
AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner會註冊一些註解處理器,註冊的方式都是使用AnnotationConfigUtils的registerAnnotationConfigProcessors方法:工具
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, @Nullable Object source) { ... if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)); } ... return beanDefs; }
最終AnnotationConfigApplicationContext
構造方法執行完成後ApplicationContext會有如下BeanDefinition:post
構造完ApplicationContext後SpringApplicaiton緊接着會加載primarySource
,上面提到 過primarySource是在運行的時候傳遞進來的(AutoConfigApplication.class),加載過程當中不貼代碼了,只要知道最終ApplicaitonContext中會多一個AutoConfigApplication
的BeanDefinition:this
總的來講SpringApplicaiton主要乾了這些事:debug
primarySource
加載進ApplicationContext最重要的一點是,如今是有一個AnnotationConfigApplicationContext裏面包含了primarySource(AutoConfigApplication)以及ConfigurationClassPostProcessor。打個斷點在ApplicaitonContext刷新以前打印下context中的bean的名稱,能夠肯定這樣說沒毛病!調試
雖然說有了primarySource和ConfigurationClassPostProcessor後處理器,仍是須要有個執行的入口。ConfigurationClassPostProcessor是BeanDefinitionRegistryPostProcessor的實現類,BeanDefinitionRegistryPostProcessor會在ApplicationContext的refresh操做時被處理:code
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { ... invokeBeanFactoryPostProcessors(beanFactory); ... } } public static void invokeBeanFactoryPostProcessors( ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) { ... //找出全部類型爲BeanDefinitionRegistryPostProcessor的bean的名稱 String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); for (String ppName : postProcessorNames) { if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); } } sortPostProcessors(currentRegistryProcessors, beanFactory); registryProcessors.addAll(currentRegistryProcessors); //執行BeanDefinitionRegistryPostProcessor invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); ... } private static void invokeBeanDefinitionRegistryPostProcessors( Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) { for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) { //調用postProcessBeanDefinitionRegistry方法 postProcessor.postProcessBeanDefinitionRegistry(registry); } }
invokeBeanDefinitionRegistryPostProcessors會調用BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法,經過斷點調試工具確認下ConfigurationClassPostProcessor有沒有在這一步被處理:
調試輸出postProcessors集合裏面有一個了ConfigurationClassPostProcessor元素,說明了ConfigurationClassPostProcessor的執行入口沒有問題。
ConfigurationClassPostProcessor首先會判斷在ApplicationContext中的bean是否被@Configuration註解標記,而後使用ConfigurationClassParser來解析@Configuration,ConfigurationClassPostProcessor的解析@Configuration的大體流程:
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); //獲取全部BeanDefinitio怕名稱 String[] candidateNames = registry.getBeanDefinitionNames(); for (String beanName : candidateNames) { BeanDefinition beanDef = registry.getBeanDefinition(beanName); //若是是full、lite則說明已經處理過的類 if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) || ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) { if (logger.isDebugEnabled()) { logger.debug("Bean definition has already been processed as a configuration class: " + beanDef); } } //檢查BeanDefinition是否有@Configuration註解 else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } } //若是沒有找到@Configuration標記的類,則返回不做處理也 if (configCandidates.isEmpty()) { return; } //對@Configuration進行排序 configCandidates.sort((bd1, bd2) -> { int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); return Integer.compare(i1, i2); }); ... ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); do { //解析@Configuration class parser.parse(candidates); parser.validate(); Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed); //讀取BeanDefinition if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses); candidates.clear(); ... } while (!candidates.isEmpty()); ... }
最後仍是經過調試工具看一下示例中的的啓動類AutoConfigApplication
沒有被處理:
圖上顯示configCandidates中有一個名稱爲autoConfigApplication的BeanDefinition的元素,說明AutoConfigApplication會被看成配置類解析,可是AutoConfigApplication並無使用@Configuration註解,爲何還會被當作配置類呢?其實@Configuration在@SpringBootApplication註解中:
紅色背景列出來的就是@Configuration註解,它是@SpringBootConfiguration的元註解。