SpringBoot启动流程之(prepareEnvironment)
- prepareEnvironment流程图:
- 上一章讲到了,listeners通过帅选,得出了需要执行的listener,并且执行了相关的onApplicationEvent
- 系统在执行完对应的starting的listeners后,便进行prepareEnvironment,代码如下
public ConfigurableApplicationContext run(String...args){ } //记录任务耗时 StopWatch stopWatch=new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context=null; Collection<SpringBootExceptionReporter> exceptionReporters=new ArrayList<>(); configureHeadlessProperty(); //加载并实例化META-INF/spring.factories下面的SpringApplicationRunListener所对应的class集合 SpringApplicationRunListenerslisteners=getRunListeners(args); //根据参数决定加载某些listener,并且start listeners.starting(); //SpringApplication的参数封装 ApplicationArguments applicationArguments=new DefaultApplicationArguments(args); //这里便是重点 ConfigurableEnvironment environment=prepareEnvironment(listeners,applicationArguments); //下面还有,这里不讲......
prepareEnvironment()方法体
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments){ // 获取或者根据一定条件创建Environment对象,这里返回的是StandardServletEnvironment() ConfigurableEnvironment environment=getOrCreateEnvironment(); //为Environment设置了系统参数以及一些相关的PropertySource configureEnvironment(environment,applicationArguments.getSourceArgs()); //对PropertySource转化为ConfigurationPropertySourcesPropertySource ConfigurationPropertySources.attach(environment); //事件发布(具体逻辑在listeners.start里面讲过了) listeners.environmentPrepared(environment); //为environment与Binder建立联系(至于为什么,我也不知道,留个????) bindToSpringApplication(environment); //是否为自定义的环境 if(!this.isCustomEnvironment){ //如果不是自定义环境,则会对environment进行格式转换 environment=new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment; }
getOrCreateEnvironment()解释:创建了当前应用适配的Environment
private ConfigurableEnvironment getOrCreateEnvironment(){ //是否存在环境 if(this.environment!=null){ return this.environment; } //根据当前应用类型,加载不同的Environment对象 //至于怎么获取当前应用类型,在构造SpringApplication时讲了 switch(this.webApplicationType){ //servlet环境 case SERVLET: return new StandardServletEnvironment(); //响应式环境 case REACTIVE: return new StandardReactiveWebEnvironment(); //非web环境 default: return new StandardEnvironment(); } } • StandardServletEnvironment#customizePropertySources(),为Environment配置几个键值对象 public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment { @Override protected void customizePropertySources(MutablePropertySources propertySources) { //往MutablePropertySources添加了名为servletContextInitParams, // servletConfigInitParams的StubPropertySource对象 propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME)); propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME)); if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) { propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME)); } //通过父类添加了名为systemEnvironment的PropertiesPropertySource对象 //通过父类添加了名为systemProperties的SystemEnvironmentPropertySource对象 super.customizePropertySources(propertySources); }
- 这里主要是获得了四个不同的键值对象,其中最后两个键值对象;systemEnvironment、systemProperties包含了主机的所有信息
- 主要信息如下
configureEnvironment(environment, applicationArguments.getSourceArgs()):模板方法,将实现委托给了configurePropertySources,configureProfiles
protectedvoidconfigureEnvironment(ConfigurableEnvironment environment,String[]args){ //ConversionService:懒汉式线程安全单例模式,管理了一系列bean的转化 if(this.addConversionService){ ConversionService conversionService=ApplicationConversionService.getSharedInstance(); environment.setConversionService((ConfigurableConversionService)conversionService); } //对 PropertySource进行管理(主要是系统属性的配置),后面会讲 configurePropertySources(environment,args); //这里主要对系统环境的配置,决定激活什么配置文件 configureProfiles(environment,args); }
- SpringApplication#configurePropertySources(对PropertySources进行优先级管理,或者添加删除一些默认配置)
protected void configurePropertySources(ConfigurableEnvironment environment,String[]args){ //获取当前系统environment的PropertySources MutablePropertySources sources=environment.getPropertySources(); //检测是否存在默认配置,如果有则添加到PropertySources中(低优先级) if(this.defaultProperties!=null&&!this.defaultProperties.isEmpty()){ sources.addLast(new MapPropertySource("",this.defdefaultPropertiesaultProperties)); } //从命令行参数读取参数,并且加入到PropertySources中 if(this.addCommandLineProperties&&args.length>0){ String name=CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME; //如果原先的PropertySources存在此命令行key if(sources.contains(name)){ PropertySource<?> source=sources.get(name); //则新建一个CompositePropertySource对象 CompositePropertySource composite=new CompositePropertySource(name); //并且添加到PropertySources composite.addPropertySource( new SimpleCommandLinePropertySource("springApplicationCommandLineArgs",args)); composite.addPropertySource(source); //然后替换掉原来的用新的对象替换掉原来的key sources.replace(name,composite); } else{ //否则直接在最前面添加命令行参数(高优先级) 如:java-jar --server.port......等参数 sources.addFirst(new SimpleCommandLinePropertySource(args)); } } }
- SpringApplication()#configureProfiles():确定激活什么样的配置文件
protected void configureProfiles(ConfigurableEnvironment environment,String[]args){ Set<String> profiles=new LinkedHashSet<>(this.additionalProfiles); profiles.addAll(Arrays.asList(environment.getActiveProfiles())); //往下看 environment.setActiveProfiles(StringUtils.toStringArray(profiles)); }
- AbstractEnvironment#setActiveProfiles
public void setActiveProfiles(String...profiles){ Assert.notNull(profiles,"Profile array must not be null"); if(logger.isDebugEnabled()){ logger.debug("Activating profiles "+Arrays.asList(profiles)); } synchronized (this.activeProfiles){ //清除自身的被激活的环境 this.activeProfiles.clear(); for(String profile:profiles){ //验证环境 validateProfile(profile); //确认需要激活的环境 this.activeProfiles.add(profile); } } }
- ConfigurationPropertySources.attach(environment):将PropertySources转化为更适配的ConfigurationPropertySourcesPropertySource
public static void attach(Environment environment){ Assert.isInstanceOf(ConfigurableEnvironment.class,environment); //获取当前environment的PropertySources MutablePropertySources sources=((ConfigurableEnvironment)environment).getPropertySources(); PropertySource<?> attached=sources.get(ATTACHED_PROPERTY_SOURCE_NAME); if(attached!=null&&attached.getSource()!=sources){ sources.remove(ATTACHED_PROPERTY_SOURCE_NAME); attached=null; } if(attached==null){ //PropertySources转化为ConfigurationPropertySources,为后续Binder.get(this.environment)打下基础 sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME, new SpringConfigurationPropertySources(sources))); } }
listeners.environmentPrepared(environment):事件的发布,分派给具体的listener执行
- 调用链路:SpringApplication->SpringApplicationRunListeners#environmentPrepared()-> EventPublishingRunListener#environmentPrepared()->SimpleApplicationEventMulticaster#multicastEvent()-> ApplicationListener#onApplicationEvent()
- 这是在执行listeners.environmentPrepared()的event类型
- 这是过滤后的listeners,也是onApplicationEvent()的具体分派类型
- 没有执行listeners.environmentPrepared():
- 执行listeners之后的:
- 可以看出在这里发布listeners之后,主要添加了两个对象,一个是random对象,一个则是配置文件application.properties的信息 (PS:这里没讲怎么读取的,因为坑太大,如果有可能,新开一坑,来写listener发布之后以及加载配置信息的流程)
bindToSpringApplication(environment);
- Binder:包含一个或者多个ConfigurationPropertySources对象的容器(这也是为什么前面需要将PropertySources转换为ConfigurationPropertySources的原因)
protected void bindToSpringApplication(ConfigurableEnvironment environment){ try{ //先为environment建立Binder对象,为SpringApplictaion设置key Binder.get(environment).bind("spring.main",Bindable.ofInstance(this)); } catch(Exception ex){ throw new IllegalStateException("Cannot bind to SpringApplication",ex); } }