• SpringBoot源码学习(三) 环境准备


    运行Spring应用程序

    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的子类listener
       SpringApplicationRunListeners listeners = getRunListeners(args);
       // 开启事件监听,通知监听者们(listener)执行相应操作
       listeners.starting();
       try {
          // 封装命令行参数
          ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
          // 准备环境
          ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
          // 配置忽略信息
          configureIgnoreBeanInfo(environment);
          // 打印banner
          Banner printedBanner = printBanner(environment);
          // 创建应用上下文
          context = createApplicationContext();
          // 从`META-INF/spring.factories`文件集中获取SpringBootExceptionReporter的子类
          exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);
          // 准备上下文
          prepareContext(context, environment, listeners, applicationArguments, printedBanner);
          // 刷新上下
          refreshContext(context);
          // 刷新上下文后置处理
          afterRefresh(context, applicationArguments);
          stopWatch.stop();
          if (this.logStartupInfo) {
             new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
          }
          listeners.started(context);
          callRunners(context, applicationArguments);
       }
       catch (Throwable ex) {
          handleRunFailure(context, ex, exceptionReporters, listeners);
          throw new IllegalStateException(ex);
       }
    
       try {
          listeners.running(context);
       }
       catch (Throwable ex) {
          handleRunFailure(context, ex, exceptionReporters, null);
          throw new IllegalStateException(ex);
       }
       return context;
    }
    

    准备环境

    通过观察者模式将环境变量,命令行参数,配置文件属性绑定到Application。

    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
          ApplicationArguments applicationArguments) {
       // 创建和配置环境
       ConfigurableEnvironment environment = getOrCreateEnvironment();
       // 配置环境
       configureEnvironment(environment, applicationArguments.getSourceArgs());
       // 将环境链接到configurationProperties属性名下
       ConfigurationPropertySources.attach(environment);
       // 发布环境准备事件
       listeners.environmentPrepared(environment);
       // 绑定到Application
       bindToSpringApplication(environment);
       if (!this.isCustomEnvironment) {
          environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                deduceEnvironmentClass());
       }
       ConfigurationPropertySources.attach(environment);
       return environment;
    }
    

    依赖图谱

    prepareEnvironment

    创建和配置环境

    根据SpringApplication构造器中推断出应用类型来实例化不同的环境。参考: SpringBoot源码学习(一) SpringApplication构造器

    private ConfigurableEnvironment getOrCreateEnvironment() {
       if (this.environment != null) {
          return this.environment;
       }
       switch (this.webApplicationType) {
       case SERVLET:
          // 标准的Servlet环境
          return new StandardServletEnvironment();
       case REACTIVE:
          // WebFlux环境
          return new StandardReactiveWebEnvironment();
       default:     
          return new StandardEnvironment();
       }
    }
    

    根据依赖关系实例化StandardServletEnvironment需实例化父类。推断出首先执行org.springframework.core.env.AbstractEnvironment#AbstractEnvironment

    public AbstractEnvironment() {
       // 创建一个新的Environment实例,在构造过程中回调customPropertySources(MutablePropertySources),以允许子类在适当时操纵PropertySource实例。
       customizePropertySources(this.propertySources);
    }
    

    自定义属性源

    org.springframework.web.context.support.StandardServletEnvironment重写了customizePropertySources(MutablePropertySources) 方法。

    @Override
    protected void customizePropertySources(MutablePropertySources propertySources) {
       /**
        * 在servletConfigInitParams中存在的属性将优先于在servletContextInitParams中的属性,
        * 并且在上述任何一个中找到的属性都优先于在jndiProperties中发现的属性。 
        * 以上任何属性均优先于StandardEnvironment超类提供的系统属性和环境变量。 
        * 与Servlet相关的属性源在此阶段作为`StubPropertySource stubs`添加,
        * 一旦实际的ServletContext对象被initPropertySources(ServletContext, ServletConfig)完全初始化变得可用。
        * servletConfigInitParams
        * servletContextInitParams
        * jndiProperties 不一定存在
        * systemProperties
        * systemEnvironment
        */
       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));
       }
       super.customizePropertySources(propertySources);
    }
    
    @Override
    protected void customizePropertySources(MutablePropertySources propertySources) {
       propertySources.addLast(
             new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
       propertySources.addLast(
             new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
    }
    

    执行完此处代码,源集合中的name为:

    servletConfigInitParams,
    servletContextInitParams,
    systemProperties,
    systemEnvironment
    

    配置环境

    protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
       if (this.addConversionService) {
          // 获取数据转换共享实例
          ConversionService conversionService = ApplicationConversionService.getSharedInstance();
          environment.setConversionService((ConfigurableConversionService) conversionService);
       }
       // 配置属性源
       configurePropertySources(environment, args);
       // 配置Profiles
       configureProfiles(environment, args);
    }
    

    配置属性源

    在此应用程序的环境中添加,删除或重新排序PropertySource。

    protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
       MutablePropertySources sources = environment.getPropertySources();
       if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
          // this.defaultProperties 不为空,将defaultProperties放入sources尾
          sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
       }
    
       // 如果命令行参数存在,判断名称为commandLineArgs的source存不存在,存在替换,不存在放入sources首,参数以命令行优先
       if (this.addCommandLineProperties && args.length > 0) {
          String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
          if (sources.contains(name)) {
             PropertySource<?> source = sources.get(name);
             CompositePropertySource composite = new CompositePropertySource(name);
             composite.addPropertySource(
                   new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
             composite.addPropertySource(source);
             sources.replace(name, composite);
          }
          else {
             sources.addFirst(new SimpleCommandLinePropertySource(args));
          }
       }
    }
    

    配置Profiles

    配置文件环境中哪些配置文件处于活动状态。在配置文件处理过程中,可以通过spring.profiles.active属性激活其他配置文件。

    protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
       // 配置文件环境中哪些配置文件处于活动状态。在配置文件处理过程中,可以通过spring.profiles.active属性激活其他配置文件。
       Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
       profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
       environment.setActiveProfiles(StringUtils.toStringArray(profiles));
    }
    
    private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
       if (System.getProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) {
          Boolean ignore = environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE);
          System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME, ignore.toString());
       }
    }
    

    链接数据源

    将环境源链接到configurationProperties属性名下

    public static void attach(Environment environment) {
       Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
       MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
       // 获取sources中名称为configurationProperties的sources,存在将并与现有sources不同内存地址,就删除configurationProperties对应的源。不存在直接将此名称添加到队首
       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) {
          sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
                new SpringConfigurationPropertySources(sources)));
       }
    }
    

    发布环境准备事件

    参考: SpringBoot源码学习(二) 发布启动事件

    void environmentPrepared(ConfigurableEnvironment environment) {
    	/**
    	 * org.springframework.boot.devtools.restart.RestartApplicationListener,
    	 * org.springframework.boot.context.config.ConfigFileApplicationListener,
    	 * org.springframework.boot.context.config.AnsiOutputApplicationListener,
    	 * org.springframework.boot.context.logging.LoggingApplicationListener,
    	 * org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,
    	 * org.springframework.boot.autoconfigure.BackgroundPreinitializer,
    	 * org.springframework.boot.context.config.DelegatingApplicationListener,
    	 * org.springframework.boot.context.FileEncodingApplicationListener
    	 */
       for (SpringApplicationRunListener listener : this.listeners) {
          listener.environmentPrepared(environment);
       }
    }
    
  • 相关阅读:
    企业如何才能“勾搭”上服务网格技术?
    行云创新:云原生加速企业释放数据价值
    行云创新:后疫情时代,云原生为酒店数字化转型破局
    行云创新CEO马洪喜荣获“2021杰出质造人物奖”
    SolarMesh发布 v1.6.1版本,再不来体验就......
    什么是云原生?如何建设云原生平台?
    行云创新联合上汽乘用车打造云原生技术平台,加快实现数字化转型
    五分钟搭建你的第一个区块链应用
    mysql 存储过程
    MySQL-binlog日志格式 binlog_format三种模式详解
  • 原文地址:https://www.cnblogs.com/chinda/p/14312495.html
Copyright © 2020-2023  润新知