以前开发一个我的项目,要花费不少工夫在搭建我的项目,配置文件上,到当初Spring Boot开箱即用,须要技术栈导入pom就能够了,技术变更带来效率提醒是微小的。有时候我会纳闷,这所有如何得来的,Spring Boot怎么摈弃war部署,摈弃繁琐xml配置。
浏览本文章须要肯定的Spring框架常识储备,最初能理解Spring如何进行Bean初始化的,至多晓得BeanDefinition之类的知识点,能力更好阅读文章。上面代码基于Spring Boot 2.7.2 、 Spring Cloud 2021.0.3。
先从我的项目启动放入入口,每一个Spring Boot 我的项目都须要main入口都要调用SpringApplication.run
开始
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class<?>[] { primarySource }, args); } public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); } public SpringApplication(Class<?>... primarySources) { this(null, primarySources); } public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //web 我的项目类型 this.webApplicationType = WebApplicationType.deduceFromClasspath(); this.bootstrapRegistryInitializers = new ArrayList<>( getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }
webApplicationType是一个枚举类,形容以后我的项目web类型
NONE: 以后我的项目不是一个web我的项目
SERVLET: 基于servlet api的传统web我的项目
REACTIVE: Spring webFlux 响应式web框架
deduceFromClasspath : 依据我的项目jar判断以后我的项目属于下面哪个一个类型,前面创立Spring 上下文对象须要用到
getSpringFactoriesInstances:从给定接口从文件META-INF/spring.factories
应用类名去加载全类名,并且返回接口所有实现类, 配置文件格式如下
org.springframework.context.ApplicationContextInitializer=\org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
这个相似JVM的SPI机制,对于Spring为什么没有应用SPI来 引入扩大实例,我猜SPI不满足多结构参数的实现类初始化,这里临时将这种机制称作:SpringFactoriesLoader加载。
BootstrapRegistryInitializer:用于初始化BootstrapRegistry的回调接口,在 应用BootstrapRegistry之前调用它。
ApplicationContextInitializer:在执行Spring工厂类调用AbstractApplicationContext.refresh(Spring 工厂外围办法bean初始化)之前初始化ConfigurableApplicationContext的回调接口。次要是做一个配置文件设置、属性设置。
ConfigurableApplicationContext 是一个SPI接口用于通过 配置形式初始化ApplicationContext 。Spring Boot作为Spring框架的集大成者上下文对象ApplicationContext往往依据不同环境有所区别的,这时很须要ApplicationContextInitializer这种接口,由不同组件依据本身状况去实现接口初始化上下文对象。
ApplicationContextInitializer接口
DelegatingApplicationContextInitializer
: 通过环境变量 context.initializer.classes
类名,加载所有ConfigurdiableApplicationContext子类,实例化,排序执行ApplicationContextInitializer接口(接口参数)。SharedMetadataReaderFactoryContextInitializer
: 注册CachingMetadataReaderFactoryPostProcessor 用于向容器注册SharedMetadataReaderFactoryBean,用于缓存Spring加载资源ContextIdApplicationContextInitializer
: 初始化ContextIdConfigurationWarningsApplicationContextInitializer
:报告@ComponentScan配置错误信息输出告警日志RSocketPortInfoApplicationContextInitializer
: 创立一个监听事件,将server.ports赋值到 local.rsocket.server.portServerPortInfoApplicationContextInitializer
: 创立web事件监听: 公布server namespace网络端口ConditionEvaluationReportLoggingListener
: 创立一个事件监听,spring初始化胜利或失败,打印相干信息。
ApplicationListener列表
EnvironmentPostProcessorApplicationListener
: 监听ApplicationEnvironmentPreparedEvent事件,执行EnvironmentPostProcessor 配置文件前置处理器,加载配置文件到ConfigurableEnvironmentAnsiOutputApplicationListener
: 监听Spring刚启动事件,从配置文件加载ansi配置。LoggingApplicationListener
: 加载日志相干配置进行初始化设置。BackgroundPreinitializer
: 通过多线程形式初始化Formatter、Validation、HttpMessageConverter、jackson、UTF-8设置。DelegatingApplicationListener
:从配置文件 key:context.listener.classes加载监听器类名并实例化注册到容器中ParentContextCloserApplicationListener
: 监听父级容器敞开事件,并且将事件传递到子级逐级传递下取。ClearCachesApplicationListener
: 革除类加器缓存FileEncodingApplicationListener
: 检测以后零碎环境的file.encoding和spring.mandatory-file-encoding设置的值是否一样,如果不一样的话,就会抛出一个IllegalStateException异样,程序启动立马进行
run办法
public ConfigurableApplicationContext run(String... args) { long startTime = System.nanoTime(); //调用BootstrapRegistryInitializer接口对上下文进行初始化 DefaultBootstrapContext bootstrapContext = createBootstrapContext(); ConfigurableApplicationContext context = null; // 设置 java.awt.headless 缺失显示设施须要CPU染指显示 configureHeadlessProperty(); //获取事件公布器实例,这里会将下面监听器实例装进公布器,监听器相似事件消费者 SpringApplicationRunListeners listeners = getRunListeners(args); //公布starting 事件 listeners.starting(bootstrapContext, this.mainApplicationClass); try { //获取所有启动参数 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); //创立配置文件对象 ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); //从配置文件中漠视bean configureIgnoreBeanInfo(environment); //Banner 配置 打印 Banner printedBanner = printBanner(environment); //应用ApplicationContextFactory 初始化ApplicationContentx Spring 工厂 context = createApplicationContext(); context.setApplicationStartup(this.applicationStartup); //配置文件对象配置 //开始对applicationContext context 进行初始化 prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); // 调用refresh //空办法 afterRefresh(context, applicationArguments); Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup); } listeners.started(context, timeTakenToStartup); //调用所有 ApplicationRunner CommandLineRunner callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, listeners); throw new IllegalStateException(ex); } try { Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime); listeners.ready(context, timeTakenToReady); } catch (Throwable ex) { handleRunFailure(context, ex, null); throw new IllegalStateException(ex); } return context; }
run()
Spring Boot框架启动流程
- 获取Java 命令行启动参数,从中提取Spring 配置参数,转换从对应变量
- 创立配置文件对象ConfigurableEnvironment ,命令行中会有profile设置,所以要依据profile加载配置文件,在执行配置文件事件
- 曾经加载好文件了,从环境变量中检测是否存在配置spring.beaninfo.ignore,如果设置,写入到ConfigurableEnvironment中
- 开始打印banner,平时看到各种banner就是在这里执行
- 开始创立ConfigurableApplicationContext ,Spring 容器工厂上下文对象
- 对刚刚创立ConfigurableApplicationContext 调用ApplicationContextInitializer 进行属性设置
- 启动Spring 容器IOC、AOP
- 公布Spring启动实现事件
- 从容器中所有ApplicationRunner CommandLineRunner在调用办法
在run办法外面就实现实现整个Spring容器启动流程了,包含Spring Cloud加载也是这里实现的。上面详细分析prepareEnvironment()
,配置文件上下文如何初始化的
prepareEnvironment
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) { // 依据webApplicationType 创立 // SERVLET => ApplicationServletEnvironment //REACTIVE=> ApplicationReactiveWebEnvironment // NONE => ApplicationEnvironment ConfigurableEnvironment environment = getOrCreateEnvironment(); // 命令行可能会有profile,能够抉择那个profile,也会将命令行参数生成一个PropertySources configureEnvironment(environment, applicationArguments.getSourceArgs()); // 增加 configurationProperties PropertySource到propertySourceList 队列最后面 ConfigurationPropertySources.attach(environment); // 执行所有SpringApplicationRunListener listeners.environmentPrepared(bootstrapContext, environment); // 将defaultProperties sources 移致队尾 DefaultPropertiesPropertySource.moveToEnd(environment); Assert.state(!environment.containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties."); // 从配置文件对应spring.main.* 属性注入 bindToSpringApplication(environment); if (!this.isCustomEnvironment) { //将类型转换器设置到environment environment = convertEnvironment(environment); } // 因为EnvironmentPostProcessor 可能加载到配置文件里,这时须要configurationProperties 放入第一 ConfigurationPropertySources.attach(environment); return environment; }
getOrCreateEnvironment() 如果以后environment如何为空,则会依据依据webApplicationType 类型抉择对应类进行初始化。大家可能好奇environment怎么可能有值呢,接着玩下看,当我剖析Spring Cloud时你就会返回environment不须要创立了。
ps: Environment 外部应用PropertySource辨别不同配置文件,每一个源配置都有本人的名字,比方零碎变量systemProperties、环境变量systemEnvironment等等。应用一个propertySourceList一个list将所有PropertySource保存起来,在队列后面永远最优先加载。
在下面写过一个监听器EnvironmentPostProcessorApplicationListener
,它解决environmentPrepared事件,应用SpringFactoriesLoader加载所有EnvironmentPostProcessor 前置处理器,其中之一ConfigDataEnvironmentPostProcessor
就是去做读取配置文件,外面还有很多逻辑解决,这里就不开展了,有趣味的同学自行去剖析代码。读取文件自身也是依据环境变量来的,这里有几个Spring内置配置
- spring.config.location 设定加载文件门路,没有则是应用类门路./、config/
- spring.config.additional-location: 加载内部文件门路,这个能够spring.config.location 共存,优先级最大
spring.config.name 设定文件名前置,默认 application
下面这些变量都是从环境变量、零碎变量中获取的,当然不会从配置文件读取到。通过设定文件门路、文件名这样形式确定加载文件,加载文件规定如下${spring.config.location}/ ${spring.config.name}-${profile}.${extension}
- extension:文件名后缀 内置反对4种,别离是: properties、yml、xml、yaml
看下ConfigurableApplicationContext 如何被初始化的
prepareContext
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { // 初始化 ConfigurableEnvironment context.setEnvironment(environment); //初始化resourceLoader ConversionService postProcessApplicationContext(context); //执行下面从SpringFactoriesLoader加载 ApplicationContextInitializer 对ConfigurableApplicationContext 属性设置 applyInitializers(context); // 调用SpringApplicationRunListener.contextPrepared 事件 listeners.contextPrepared(context); // 执行BootstrapContextClosedEvent 事件 bootstrapContext.close(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } //上面增加特定单例对象,为Spring初始化bean IOC 解决必要的bean ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } // 这里曾经从配置文件加载 设置到本身属性上了,这时设置给上下文对象 // allowCircularReferences 容许同名bean笼罩 lazyInitialization 对所有bean应用懒加载 if (beanFactory instanceof AbstractAutowireCapableBeanFactory) { ((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences); if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } } if (this.lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); } // 这个前置处理器次要作用就是将配置defaultProperties 移到队尾 context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context)); // Load the sources 这里有启动类 Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); // 初始化 BeanDefinitionLoader 并将启动类注册成BeanDefinition load(context, sources.toArray(new Object[0])); // 所有监听器执行contextLoaded 事件 listeners.contextLoaded(context); }````在这里实现了Spring 容器初始化,下一步就是启动了。### bean初始化其实我始终很好奇@Configuration这个注入如何实现配置类,还有还么多Class要被Spring进行初始化,如何变成BeanDefinition最初变成bean。我确定从`AbstractApplicationContext.refresh()`debug,终于被我发现Spring魔法,在`invokeBeanFactoryPostProcessors()`在执行invokeBeanFactoryPostProcessors办法中回去获取`BeanDefinitionRegistryPostProcessor`类型内置对象,并且执行所有实现类。- `BeanDefinitionRegistryPostProcessor`: 你能够了解成BeanDefinition注册前置处理器,次要就是生成BeanDefinition,再还给容器。在Spring还没有初始化bean时,这个接口运行实现类去初始化BeanDefinition再交还给Spring工厂对象,简白点就是这个对象会创立BeanDefinition,交给Spring,后续进行初始化bean。上面要解说其中一个实现类`ConfigurationClassPostProcessor`#### postProcessBeanFactory创立postProcessBeanFactory
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { int registryId = System.identityHashCode(registry); if (this.registriesPostProcessed.contains(registryId)) { //这个办法只能执行一次,通过记录上下文id标记执行 throw new IllegalStateException( "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry); } if (this.factoriesPostProcessed.contains(registryId)) { throw new IllegalStateException( "postProcessBeanFactory already called on this post-processor against " + registry); } this.registriesPostProcessed.add(registryId); // 解析Class 生成BeanDefinition processConfigBeanDefinitions(registry);}/** * Build and validate a configuration model based on the registry of * {@link Configuration} classes. */public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); String[] candidateNames = registry.getBeanDefinitionNames(); //candidateNames 为后期应用BeanDefinitionRegistry 增加进去单例对象,除了领有Spring 工厂对象外还有 // SpringBoot main 启动类 这里能起到作用就是Spring Boot main 函数 for (String beanName : candidateNames) { BeanDefinition beanDef = registry.getBeanDefinition(beanName); if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) { if (logger.isDebugEnabled()) { logger.debug("Bean definition has already been processed as a configuration class: " + beanDef); } } // 查看beanDef 是不是配置类,带有@Configuration都算 else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } } // 这里不存在没有配置类,只有配置@SpringBootApplication Class 就是一个配置类 if (configCandidates.isEmpty()) { return; } // Sort by previously determined @Order value, if applicable configCandidates.sort((bd1, bd2) -> { int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); return Integer.compare(i1, i2); }); // Detect any custom bean name generation strategy supplied through the enclosing application context SingletonBeanRegistry sbr = null; if (registry instanceof SingletonBeanRegistry) { sbr = (SingletonBeanRegistry) registry; if (!this.localBeanNameGeneratorSet) { BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton( AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR); if (generator != null) { this.componentScanBeanNameGenerator = generator; this.importBeanNameGenerator = generator; } } } if (this.environment == null) { this.environment = new StandardEnvironment(); } // Parse each @Configuration class // ConfigurationClassParser 看名字就晓得,这是一个解析@Configuration 解析类 // 将解析Class 工作专门委派给parse去做了,解析后的后果会变成 ConfigurationClass 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 { //这里是一个循环 StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse"); parser.parse(candidates); parser.validate(); //曾经将所有配置类全副解析进去 变成ConfigurationClass Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed); // 删除曾经解析过 // Read the model and create bean definitions based on its content if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } this.reader.loadBeanDefinitions(configClasses); //将所有ConfigurationClass 转化BeanDefinition ,并注册到容器中 alreadyParsed.addAll(configClasses); //增加曾经注册过的,下面删除对应 processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end(); candidates.clear(); // 当ConfigurationClassParser 解析出ConfigurationClass 就会大于candidateNames if (registry.getBeanDefinitionCount() > candidateNames.length) { String[] newCandidateNames = registry.getBeanDefinitionNames(); Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames)); Set<String> alreadyParsedClasses = new HashSet<>(); for (ConfigurationClass configurationClass : alreadyParsed) { alreadyParsedClasses.add(configurationClass.getMetadata().getClassName()); } for (String candidateName : newCandidateNames) { if (!oldCandidateNames.contains(candidateName)) { BeanDefinition bd = registry.getBeanDefinition(candidateName); // bd 就是一个配置类 // bd 曾经注册到容器中,然而不是在ConfigurationClassParser 解析进去的后果,则阐明bd并没有通过解析生成 // 可能为第三方 BeanDefinitionRegistryPostProcessor 生成BeanDefinition,退出到candidates 再次进入循环中 //被ConfigurationClassParser 解析,能够生成更多BeanDefinition if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) { candidates.add(new BeanDefinitionHolder(bd, candidateName)); } } } candidateNames = newCandidateNames; } } while (!candidates.isEmpty()); // 当所有BeanDefinition 都曾经被解析完了,循环就能够退出了 //上面省略}
看完下面的代码,ConfigurationClassPostProcessor就是Spring将带有@Configuration 标记Class通过一系列解决生成BeanDefinition的机制。在@SpringBootApplication 中有个一个@EnableAutoConfiguration带有@Import(AutoConfigurationImportSelector.class),这个会被ConfigurationClassPostProcessor解析加载。其中AutoConfigurationImportSelector应用SpringFactoriesLoader加载,会将所有@EnableAutoConfiguration的配置类全副都加载ClassName,能够让Spring Boot 加载ScanPackage 根底包门路之外的配置类,再通过@ConditionalOnBean、@ConditionalOnProperty这类注解,依据Class、配置判断是否进行解析。也就是说Spring Boot一开始就曾经获取到所有配置类,只有当符合条件时才会进入解析、加载、实例化。### Spring Cloud下面说了Spring Boot自动化配置接下来就是Spring Cloud方面,看了下面源码,发现没有代码无关Spring Cloud,当初还不晓得配置核心的配置如何作用到曾经开始运行Spring 容器中。在开始剖析代码之前,先简略看一个例子能够看到applicatioinContext 有一个父级上下文,而这个就是Spring Cloud 上下文对象。看到这个是不是很惊奇,这个父级上下文在哪里初始化的呢,从代码角度去看了。下面剖析过ApplicationListener监听器中,在Spring Cloud lib jar中有一个实现类BootstrapApplicationListener,通过它来启动Spring Cloud。
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { ConfigurableEnvironment environment = event.getEnvironment(); // 两个条件 environment 配置 spring.cloud.bootstrap.enabled 或者某个类是否存在,其实就是 spring-cloud-starter-bootstrap jar class // 配置 spring.config.use-legacy-processing 这个配置是用来兼容旧版本配置文件加载 //我这里环境引入spring-cloud-starter-bootstrap 第一个条件返回true,第二条件不必判断 if (!bootstrapEnabled(environment) && !useLegacyProcessing(environment)) { return; } // don't listen to events in a bootstrap context // 判断environment 是否曾经存在bootstrap 文件,曾经加载过不须要往下执行了 //当父级初始化也会执行监听器事件,到时来到这里时,父级监听器不会往下执行了 if (environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) { return; } ConfigurableApplicationContext context = null; // 默认配置文件名,没有在环境变量配置默认就是bootstrap String configName = environment.resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}"); for (ApplicationContextInitializer<?> initializer : event.getSpringApplication().getInitializers()) { if (initializer instanceof ParentContextApplicationContextInitializer) { //在曾经存在ParentContextApplicationContextInitializer 中返回父级容器 context = findBootstrapContext((ParentContextApplicationContextInitializer) initializer, configName); } } if (context == null) { //当下面ParentContextApplicationContextInitializer 没有执行就会走上面初始化父级容器办法 // 这里会返回父级容器,也就是Spring Cloud 上下文对象 context = bootstrapServiceContext(environment, event.getSpringApplication(), configName); event.getSpringApplication().addListeners(new CloseContextOnFailureApplicationListener(context)); } //从父级容器中获取ApplicationContextInitializer 交给SpringApplication //父级生成ApplicationContextInitializer 用于增强子类 apply(context, event.getSpringApplication(), environment);}
这个监听器次要依据配置文件信息来启动Spring Cloud组件,如果没有相应的配置依据我的项目环境来,看下Spring Cloud上下文如何被初始化进去的。
private ConfigurableApplicationContext bootstrapServiceContext(ConfigurableEnvironment environment, final SpringApplication application, String configName) { ConfigurableEnvironment bootstrapEnvironment = new AbstractEnvironment() { }; MutablePropertySources bootstrapProperties = bootstrapEnvironment.getPropertySources(); String configLocation = environment.resolvePlaceholders("${spring.cloud.bootstrap.location:}"); String configAdditionalLocation = environment .resolvePlaceholders("${spring.cloud.bootstrap.additional-location:}"); Map<String, Object> bootstrapMap = new HashMap<>(); // 应用代码生成一个Spring Cloud加载文件的配置信息,规定相似下面加载applicaton 配置 bootstrapMap.put("spring.config.name", configName); bootstrapMap.put("spring.main.web-application-type", "none"); if (StringUtils.hasText(configLocation)) { bootstrapMap.put("spring.config.location", configLocation); } if (StringUtils.hasText(configAdditionalLocation)) { bootstrapMap.put("spring.config.additional-location", configAdditionalLocation); } //将加载文件的配置信息放入配置文件上下文 environment bootstrapProperties.addFirst(new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap)); for (PropertySource<?> source : environment.getPropertySources()) { if (source instanceof StubPropertySource) { continue; } bootstrapProperties.addLast(source); } // TODO: is it possible or sensible to share a ResourceLoader? // SpringApplicationBuilder 为SpringApplication 包装类,从新生成SpringApplication来创立ApplicationContext 上下文 SpringApplicationBuilder builder = new SpringApplicationBuilder().profiles(environment.getActiveProfiles()) .bannerMode(Mode.OFF).environment(bootstrapEnvironment) // Don't use the default properties in this builder .registerShutdownHook(false).logStartupInfo(false).web(WebApplicationType.NONE); final SpringApplication builderApplication = builder.application(); if (builderApplication.getMainApplicationClass() == null) { builder.main(application.getMainApplicationClass()); } if (environment.getPropertySources().contains("refreshArgs")) { builderApplication.setListeners(filterListeners(builderApplication.getListeners())); } / BootstrapImportSelectorConfiguration builder.sources(BootstrapImportSelectorConfiguration.class); // 这里将调用SpringApplication.run 下面曾经剖析, final ConfigurableApplicationContext context = builder.run(); context.setId("bootstrap"); //这里增加AncestorInitializer 是一个ApplicationContextInitializer 实现类,目标就是让子applicationContext 和父级关联起来 addAncestorInitializer(application, context); //以后environment 为子集配置对象,这里要删除掉父级加载文件信息 bootstrapProperties.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME); //将 springCloudDefaultProperties 配置文件信息copy到environment 中 mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties); return context;}
当初所有代码都看完了,咱们来理一理整一个流程就会清晰明了。在BootstrapApplicationListener中会依据配置文件或者是我的项目环境jar来是否启动加载bootstrap配置文件。先从生成加载Spring Cloud配置信息,应用SpringApplicationBuilder来构建SpringApplication对象,然后执行SpringApplication.run 办法,这个代码咱们曾经剖析过了,初始化Spring容器上下文对象,而后进入外围refresh办法执行IOC。SpringApplicationBuilder结构SpringApplication 中没有像咱们写启动类main办法,会设置启动类Class。所以被ConfigurationClassPostProcessor解析BeanDefinition,并没有@SpringApplication 这个注解,所以这个Spring Cloud 工厂没有获取到basepackae、@EnableAutoConfiguration这些货色。依据下面代码晓得Spring Cloud将BootstrapImportSelectorConfiguration 作为BeanDefinition交给ConfigurationClassPostProcessor,这样父级容器只有加载BootstrapConfiguration标记类,父级bean和子级bean互相隔离。这样父级容器就能够去启动与Spring Cloud无关的bean。当Spring Cloud容器曾经实现bean初始化后,再来执行SpringApplicaton.run 启动Spring 容器创立。这样在子级启动之前曾经将配置核心的配置对应的对象曾经创立进去了。再通过ApplicationContextInitializer接口将配置对象加载ConfigurableEnvironment中。这里应用较短的篇幅来剖析Spring Boot这个框架如何工作,站在本人的思维上,应用3个知识点来展现Spring Boot技术细节实现。第一个从SpringApplication.run理解Spring两大工厂对象ConfigurableApplicationContext、ConfigurableEnvironment如何初始化解决进去的,配置文件如何被加载的,加载规定,知识点SpringFactoriesLoader机制,如果要做Spring Boot组件必须要这个。理解了Spring Boot ApplicationContextInitializer、ApplicationListener这些接口,还有SpringApplicationRunListener为整个Spring Boot事件监听器,对应整个框架的不同阶段解决。第二简略剖析了Spring容器启动时如何生成BeanDefinition的机制实现类:BeanDefinitionRegistryPostProcessor,理解了Spring Boot组件如何被加载、实例化,这个依赖启动类的注解。最初Spring Cloud组件如何加载实例化,这个依赖于后面两个。