关于我们

质量为本、客户为根、勇于拼搏、务实创新

< 返回新闻公共列表

prepareEnvironment

发布时间:2023-06-26 13:00:24

SpringBoot启动流程之(prepareEnvironment)

  • prepareEnvironment流程图:
  • 上一章讲到了,listeners通过帅选,得出了需要执行的listener,并且执行了相关的onApplicationEvent
  • 系统在执行完对应的starting的listeners后,便进行prepareEnvironment,代码如下

 

public ConfigurableApplicationContext run(String...args){  }  //记录任务耗时  StopWatch stopWatch=new StopWatch();  stopWatch.start();  ConfigurableApplicationContext context=null;  CollectionexceptionReporters=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){  Setprofiles=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);  }  }

/template/Home/leiyu/PC/Static