zoukankan      html  css  js  c++  java
  • 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);
       }
    }
    
  • 相关阅读:
    WF4.0 Beta1 自定义跟踪
    WF4.0 Beta1 流程设计器与Activity Designer
    新版本工作流平台的 (二) 权限算法(组织结构部分)
    WF4.0 Beta1 WorkflowInvoker
    WF4.0 基础篇 (十) Collection 集合操作
    WF4.0 基础篇 (十五) TransactionScope 事物容器
    WF4.0 基础篇 (六) 数据的传递 Arguments 参数
    WF4B1 的Procedural Activity 之InvokeMethod , InvokeMethod<T> 使用
    WF4.0 Beta1 异常处理
    WF4.0 Beta1 变量 Variables
  • 原文地址:https://www.cnblogs.com/chinda/p/14312495.html
Copyright © 2011-2022 走看看