Spring源码4:prepareEnvironment()准备环境

上篇回顾

上一篇封装命令行参数DefaultApplicationArguments主要作用是处理启动类main函数的参数, 将其封装为一个DefaultApplicationArguments对象, 为prepareEnvironment提供参数


目录

1. prepareEnvironment()准备环境
2. 获取或者创建环境getOrCreateEnvironment()
    2.1 AbstractEnvironment
    2.2 StandardEnvironment
    2.3 StandardServletEnvironment
3. 配置环境configureEnvironment()
     3.1 配置属性configurePropertySources
     3.2 配置profile
4. 发布ApplicationEnvironmentPreparedEvent事件
     4.1 ConfigFileApplicationListener
     4.2 AnsiOutputApplicationListener
     4.3 LoggingApplicationListener
     4.4 ClasspathLoggingApplicationListener
     4.5 BackgroundPreinitializer
     4.6 DelegatingApplicationListener
     4.7 FileEncodingApplicationListener
5. bindToSpringApplication绑定环境
6. 环境转换EnvironmentConverter
7. ConfigurationPropertySources.attach(environment)
8. 总结

1. prepareEnvironment()准备环境

这一步的主要作用按顺序加载命令行参数, 系统参数和外部配置文件, 创建并配置Web环境, 获取profiles.active属性, 并发布ApplicationEnvironmentPreparedEvent事件, 之后获取属性时, 按顺序获取, 获取到就立即返回, 实现了属性之间的合理加载与替换

public class SpringApplication {
  
    public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        configureHeadlessProperty();
        SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting();
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                    args);
            //本文重点
            //准备环境变量
            ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);
    }

    //准备环境方法
    private ConfigurableEnvironment prepareEnvironment(
            SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments) {
        //获取或者创建环境
        ConfigurableEnvironment environment = getOrCreateEnvironment();
        //配置环境
        configureEnvironment(environment, applicationArguments.getSourceArgs());
        //发布环境准备事件
        listeners.environmentPrepared(environment);
        //Application绑定环境
        bindToSpringApplication(environment);
        
        if (!this.isCustomEnvironment) {
            //环境转换
            //如果environment.class和模块使用的EnvironmentClass()不一致
            //那么转换成一样的
            environment = new EnvironmentConverter(getClassLoader())
                    .convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
        }
        //将环境依附到PropertySources
        ConfigurationPropertySources.attach(environment);
        return environment;
    }
}

2.获取或者创建环境getOrCreateEnvironment()

我们的项目的webApplicationType为SERVLET, 所以我们环境是StandardServletEnvironment

//获取或创建环境
private ConfigurableEnvironment getOrCreateEnvironment() {
    if (this.environment != null) {
        return this.environment;
    }
    switch (this.webApplicationType) {
    case SERVLET:
        //SERVLET环境
        return new StandardServletEnvironment();
    case REACTIVE:
        //REACTIVE环境
        return new StandardReactiveWebEnvironment();
    default:
        return new StandardEnvironment();
    }
}

StandardServletEnvironment类继承关系如下, 所以我们首先分析抽象父类AbstractEnvironment的代码, 然后分析StandardEnvironment代码, 最后分析StandardServletEnvironment

2.1 AbstractEnvironment

定义了子类需要实现的类, 并通过模板方法, 在构造函数中, 调用子类的customizePropertySources()方法, 将环境配置全部放入this.propertySources中, AbstractEnvironment实现了getActiveProfiles和setActiveProfiles方法, 分别用来获取和设备spring.profiles.active属性的配置

//抽象环境类
public abstract class AbstractEnvironment implements ConfigurableEnvironment {
    
    //配置是否允许获取SystemEnvironment的配置
    public static final String IGNORE_GETENV_PROPERTY_NAME = "spring.getenv.ignore";

    //spring启动profile
    public static final String ACTIVE_PROFILES_PROPERTY_NAME = "spring.profiles.active";

    //spring默认profile
    public static final String DEFAULT_PROFILES_PROPERTY_NAME = "spring.profiles.default";

    //默认的profile名称
    protected static final String RESERVED_DEFAULT_PROFILE_NAME = "default";
    
    //用来维护属性列表
    private final MutablePropertySources propertySources = new MutablePropertySources();
 
    //构造函数
    //调用了customizePropertySources方法
    //customizePropertySources方法由子类实现
    public AbstractEnvironment() {
        customizePropertySources(this.propertySources);
    }
    
    //空方法
    //鼓励子类继承实现
    protected void customizePropertySources(MutablePropertySources propertySources) {}
    
    //获取系统属性
   public Map<String, Object> getSystemEnvironment() {
        if (suppressGetenvAccess()) {
            //如果spring.getenv.ignore配置为true,
            //那么返回空map
            return Collections.emptyMap();
        }
        try {
            //返回系统属性
            return (Map) System.getenv();
        }
        catch (AccessControlException ex) {
            //...
        }
    }
    
    /**
     * 获取activeProfiles
     */
    @Override
    public String[] getActiveProfiles() {
        return StringUtils.toStringArray(doGetActiveProfiles());
    }

    /**
     * 获取activeProfiles
     */
    protected Set<String> doGetActiveProfiles() {
        //上锁
        synchronized (this.activeProfiles) {
            if (this.activeProfiles.isEmpty()) {
                //如果activeProfiles为空
                //那么从propertySources中获取
                //并且进行了占位符的处理
                String profiles = getProperty(ACTIVE_PROFILES_PROPERTY_NAME);
                if (StringUtils.hasText(profiles)) {
                    //profile保存到activeProfiles成员变量中
                    setActiveProfiles(StringUtils.commaDelimitedListToStringArray(
                            StringUtils.trimAllWhitespace(profiles)));
                }
            }
            return this.activeProfiles;
        }
    }

    @Override
    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) {
            //清空activeProfiles
            this.activeProfiles.clear();
            for (String profile : profiles) {
                validateProfile(profile);
                //重新添加到activeProfiles变量中
                this.activeProfiles.add(profile);
            }
        }
    }
}
2.2 StandardEnvironment

StandardEnvironment继承了AbstractEnvironment, customizePropertySources代码执行步骤有:

  • 先添加数据systemProperties(系统属性)到父类的propertySource末尾
  • 再添加systemEnvironment(系统环境变量)维护到父类的propertySource末尾
public class StandardEnvironment extends AbstractEnvironment {
    //环境变量
    public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";

    //系统属性
    public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";

    //重写父类customizePropertySources方法
    //在实例化对象的过程中调用
    @Override
    protected void customizePropertySources(MutablePropertySources propertySources) {
        //获取systemProperties(系统属性), 添加到propertySources的末尾
        propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
        
        //获取systemEnvironment(系统环境变量), 添加到propertySources的末尾
        propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
    }
}
2.3 StandardServletEnvironment

我们获取的环境是一个StandardServletEnvironment实例, 实例化StandardServletEnvironment的步骤有3个步骤

  1. 调用抽象父类AbstractEnvironment的构造函数
  2. 调用当前类的customizePropertySources方法
    • propertySources列表末尾添加一个名称为servletConfigInitParams的空配置
    • propertySources列表末尾再添加一个名称为servletContextInitParams的空配置
    • 如果jndi可用, propertySources列表末尾末尾在添加一个名称为jndiProperties的空配置, 由于我们没有使用jndi, 所以不会添加该配置
  3. 调用父类StandardEnvironment的customizePropertySources方法
    • propertySources末尾添加systemProperties(系统属性)
    • propertySources末尾获取systemEnvironment(系统环境变量)

所以当前StandardServletEnvironment对象的propertySources, 按顺序排列为servletConfigInitParams, servletContextInitParams, systemProperties, systemEnvironment

public class StandardServletEnvironment对象的 extends StandardEnvironment implements ConfigurableWebEnvironment {

    //servlet容器初始化参数
    public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams";

    //servlet配置初始化参数
    public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams";

    //jndi属性
    public static final String JNDI_PROPERTY_SOURCE_NAME = "jndiProperties";


    @Override
    protected void customizePropertySources(MutablePropertySources propertySources) {
        //末尾添加一个名称为servletContextInitParams的空配置
        propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
        //末尾再添加一个名称为servletContextInitParams的空配置
        propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
        if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
            //如果jndi配置可用
            //末尾在添加一个名称为jndiProperties的空配置
            propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
        }
        //调用父类StandardEnvironment的customizePropertySources方法
        super.customizePropertySources(propertySources);
    }
}

public abstract class PropertySource<T> {
    public static class StubPropertySource extends PropertySource<Object> {
        //source为一个空object
        public StubPropertySource(String name) {
            super(name, new Object());
        }
        @Override
        @Nullable
        public String getProperty(String name) {
            return null;
        }
    }

    //私有静态内部类
    //标记类,用于实现排序
    static class ComparisonPropertySource extends StubPropertySource {}
}

3.配置环境configureEnvironment()

首先, 为environment配置共享的类型转换服务, 然后将defaultProperties和命令行参数分别添加到environment的propertySources中

protected void configureEnvironment(ConfigurableEnvironment environment,
            String[] args) {
        if (this.addConversionService) {
            //默认为true
            //environment中配置各个转换类
            ConversionService conversionService = ApplicationConversionService
                    .getSharedInstance();
            environment.setConversionService(
                    (ConfigurableConversionService) conversionService);
        }
        //3.1配置属性
        configurePropertySources(environment, args);
        //3.2配置profile
        configureProfiles(environment, args);
    }
3.1 配置属性configurePropertySources
  • 如果默认属性defaultProperties不为空,那么将会添加到environment的propertySources末尾
  • 如果当前environment的propertySources包含commandLineArgs命令行参数, 那么替换为springApplicationCommandLineArgs
  • 如果当前environment的propertySources不包含commandLineArgs, 那么添加一个commandLineArgs到propertySources的首部, SimpleCommandLinePropertySource的代码解析在封装命令行参数DefaultApplicationArguments已经分析过了
  • 由于我们没有配置defaultProperties和commandLineArgs, 所以我们只添加了一个commandLineArgs到propertySources首部
  • 当前propertySources的顺序为:
    • commandLineArgs, servletConfigInitParams, servletContextInitParams, systemProperties, systemEnvironment
//传入参数为StandardServletEnvironment和命令行参数
protected void configurePropertySources(ConfigurableEnvironment environment,
            String[] args) {,
        //调用AbstractEnvironment的getPropertySources()方法
        //获取之前配置的所有属性
        MutablePropertySources sources = environment.getPropertySources();
        if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
            //如果this.defaultProperties不为null
            //那么添加defaultProperties到propertySources的末尾
            sources.addLast(
                    new MapPropertySource("defaultProperties", this.defaultProperties));
        }
        if (this.addCommandLineProperties && args.length > 0) {
            //如果存在命令行参数
            String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
            if (sources.contains(name)) {
                //如果sources中包含了"commandLineArgs",
                //那么将其替换为"springApplicationCommandLineArgs"
                PropertySource<?> source = sources.get(name);
                //先将"commandLineArgs"修改为null,
                CompositePropertySource composite = new CompositePropertySource(name);
                //然后新增一个PropertySource
                //name为"springApplicationCommandLineArgs",
                //source不变
                composite.addPropertySource(new SimpleCommandLinePropertySource(
                        "springApplicationCommandLineArgs", args));
                composite.addPropertySource(source);
                //替换
                sources.replace(name, composite);
            }
            else {
                //如果propertySources的中不包含"commandLineArgs"
                //将命令行参数放在propertySources的首位
                sources.addFirst(new SimpleCommandLinePropertySource(args));
            }
        }
    }
3.2 配置profile

将this.additionalProfiles和environment.getActiveProfiles()组合到一起, 重新赋值给environment的activeProfiles

//传入参数为StandardServletEnvironment和命令行参数
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
    //调用的是AbstractEnvironment的getActiveProfiles()方法
    environment.getActiveProfiles();
    Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
    //再次获取和配置profile
    profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
    //设置environment的profile
    environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}

4.发布ApplicationEnvironmentPreparedEvent事件

  1. listeners.environmentPrepared(environment), 代码之前已经分析过了
  2. 对该事件该兴趣的监听器有
    • ConfigFileApplicationListener
    • AnsiOutputApplicationListener
    • LoggingApplicationListener
    • ClasspathLoggingApplicationListener
    • BackgroundPreinitializer
    • DelegatingApplicationListener
    • FileEncodingApplicationListener
4.1 ConfigFileApplicationListener

调用SpringFactoriesLoader.loadFactories()方法, 获取EnvironmentPostProcessor的子类列表, 然后将自己加入到子类列表中, 然后按@Order注解排序, 然后调用各个EnvironmentPostProcessor的postProcessEnvironment方法, 获取到的EnvironmentPostProcessor子类有

  1. SystemEnvironmentPropertySourceEnvironmentPostProcessor
  2. SpringApplicationJsonEnvironmentPostProcessor
  3. CloudFoundryVcapEnvironmentPostProcessor
  4. ConfigFileApplicationListener
public class ConfigFileApplicationListener
        implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {       
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationEnvironmentPreparedEvent) {
            //处理environmentPrepared事件
            onApplicationEnvironmentPreparedEvent(
                    (ApplicationEnvironmentPreparedEvent) event);
        }
        if (event instanceof ApplicationPreparedEvent) {
            //之后处理applicationPrepared事件
            onApplicationPreparedEvent(event);
        }
    }

    private void onApplicationEnvironmentPreparedEvent(
            ApplicationEnvironmentPreparedEvent event) {
        List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
        //将this添加到postProcessors中
        postProcessors.add(this);
        //通过注解Order排序
        AnnotationAwareOrderComparator.sort(postProcessors);
        for (EnvironmentPostProcessor postProcessor : postProcessors) {
            //执行每个postProcessor的postProcessEnvironment方法
            postProcessor.postProcessEnvironment(event.getEnvironment(),
                    event.getSpringApplication());
        }
    }

    List<EnvironmentPostProcessor> loadPostProcessors() {
        //这个方法在初始化SpringApplication实例已经讲过了
        //获取到的EnvironmentPostProcessor列表有
        //SystemEnvironmentPropertySourceEnvironmentPostProcessor
        //SpringApplicationJsonEnvironmentPostProcessor
        //CloudFoundryVcapEnvironmentPostProcessor
        return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class,
                getClass().getClassLoader());
    }
}
SystemEnvironmentPropertySourceEnvironmentPostProcessor

获取名称为systemEnvironment的属性, 替换为OriginAwareSystemEnvironmentPropertySource类型的PropertySource

public class SystemEnvironmentPropertySourceEnvironmentPostProcessor
        implements EnvironmentPostProcessor, Ordered {
        
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment,
            SpringApplication application) {
        //名称为systemEnvironment的属性
        String sourceName = StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME;
        PropertySource<?> propertySource = environment.getPropertySources()
                .get(sourceName);
        if (propertySource != null) {
            //替换属性
            replacePropertySource(environment, sourceName, propertySource);
        }
    }
    //替换属性
    private void replacePropertySource(ConfigurableEnvironment environment,
            String sourceName, PropertySource<?> propertySource) {
        Map<String, Object> originalSource = (Map<String, Object>) propertySource
                .getSource();
        //将systemEnvironment的属性转换为OriginAwareSystemEnvironmentPropertySource类型
        SystemEnvironmentPropertySource source = new OriginAwareSystemEnvironmentPropertySource(
                sourceName, originalSource);
        //然后替换原有的属性
        environment.getPropertySources().replace(sourceName, source);
    }
}
SpringApplicationJsonEnvironmentPostProcessor

获取AbstractEnvironment的成员变量PropertySources中名称为spring.application.json的属性, 将其转换为Map, 添加到jndiProperties或者systemProperties属性之前

public class SpringApplicationJsonEnvironmentPostProcessor
        implements EnvironmentPostProcessor, Ordered {
        
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment,
            SpringApplication application) {
        MutablePropertySources propertySources = environment.getPropertySources();
        //将环境中spring.application.json配置转化为JsonPropertySource
        //然后添加到属性中
        propertySources.stream().map(JsonPropertyValue::get).filter(Objects::nonNull)
                .findFirst().ifPresent((v) -> processJson(environment, v));
    }

    private void processJson(ConfigurableEnvironment environment,
            JsonPropertyValue propertyValue) {
        //处理json
        JsonParser parser = JsonParserFactory.getJsonParser();
        Map<String, Object> map = parser.parseMap(propertyValue.getJson());
        if (!map.isEmpty()) {
            //默认放到jndiProperties 或者systemProperties之前
            addJsonPropertySource(environment,
                    new JsonPropertySource(propertyValue, flatten(map)));
        }
    }
}
CloudFoundryVcapEnvironmentPostProcessor

如果environment激活了 Cloud Founry, 那么在commandLineArgs属性之后, 添加一个vcap属性的配置

public class CloudFoundryVcapEnvironmentPostProcessor
        implements EnvironmentPostProcessor, Ordered {
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment,
            SpringApplication application) {
        //如果environment激活了 Cloud Founry
        if (CloudPlatform.CLOUD_FOUNDRY.isActive(environment)) {
            //那么添加对Cloud Founry的支持
            Properties properties = new Properties();
            JsonParser jsonParser = JsonParserFactory.getJsonParser();
            addWithPrefix(properties,
                    getPropertiesFromApplication(environment, jsonParser),
                    "vcap.application.");
            addWithPrefix(properties, getPropertiesFromServices(environment, jsonParser),
                    "vcap.services.");
            //然后在environment添加一个vcap的配置
            MutablePropertySources propertySources = environment.getPropertySources();
            if (propertySources.contains(
                    CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME)) {
                propertySources.addAfter(
                        CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME,
                        new PropertiesPropertySource("vcap", properties));
            }
            else {
                propertySources
                        .addFirst(new PropertiesPropertySource("vcap", properties));
            }
        }
    }
}
ConfigFileApplicationListener

在systemEnvironment属性之后, 添加random属性

public class ConfigFileApplicationListener
        implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {
    
    //加载配置文件的默认路径   
    private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";

    
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment,
            SpringApplication application) {
        addPropertySources(environment, application.getResourceLoader());
    }
    
    protected void addPropertySources(ConfigurableEnvironment environment,
            ResourceLoader resourceLoader) {
        //在systemEnvironment属性之后,添加random属性
        RandomValuePropertySource.addToEnvironment(environment);
        //读取配置文件
        new Loader(environment, resourceLoader).load();
    }
    
    //内部类, 加载配置文件 
    private class Loader {

        Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
            this.environment = environment;
            this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(
                    this.environment);
            //resourceLoader实例化为DefaultResourceLoader
            this.resourceLoader = (resourceLoader != null) ? resourceLoader
                    : new DefaultResourceLoader();
            
            //PropertiesPropertySourceLoader加载properties文件
            //YamlPropertySourceLoader加载yml文件
            this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(
                    PropertySourceLoader.class, getClass().getClassLoader());
        }

        public void load() {
            this.profiles = new LinkedList<>();
            this.processedProfiles = new LinkedList<>();
            this.activatedProfiles = false;
            this.loaded = new LinkedHashMap<>();
            //获取profile属性,添加到this.profiles列表中
            initializeProfiles();
            while (!this.profiles.isEmpty()) {
                Profile profile = this.profiles.poll();
                if (profile != null && !profile.isDefaultProfile()) {
                    addProfileToEnvironment(profile.getName());
                }
                //遍历profile
                //加载文件
                load(profile, this::getPositiveProfileFilter,
                        addToLoaded(MutablePropertySources::addLast, false));
                this.processedProfiles.add(profile);
            }
            //将profile属性加到environment中
            resetEnvironmentProfiles(this.processedProfiles);
            //获取配置, 加入到this.loaded中
            load(null, this::getNegativeProfileFilter,
                    addToLoaded(MutablePropertySources::addFirst, true));
            //将this.loaded按顺序添加到environment的propertySources中
            //如果存在defaultProperties,放在defaultProperties之前
            //如果不存在defaultProperties,直接添加到最后
            addLoadedPropertySources();
        }
    }
            
}
4.2 AnsiOutputApplicationListener

字符输出监听器, 用于调整控制台显示的打印字符的各种颜色

public class AnsiOutputApplicationListener
        implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {

    @Override
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
        ConfigurableEnvironment environment = event.getEnvironment();
        //控制台打印字符显示各种颜色
        //例如:设置spring.output.ansi.enabled=ALWAYS
        Binder.get(environment)
                .bind("spring.output.ansi.enabled", AnsiOutput.Enabled.class)
                .ifBound(AnsiOutput::setEnabled);
        AnsiOutput.setConsoleAvailable(environment
                .getProperty("spring.output.ansi.console-available", Boolean.class));
    }
}
4.3 LoggingApplicationListener

日志监听器, 初始化日志配置

public class LoggingApplicationListener implements GenericApplicationListener {
    //处理EnvironmentPrepared事件
    private void onApplicationEnvironmentPreparedEvent(
            ApplicationEnvironmentPreparedEvent event) {
        if (this.loggingSystem == null) {
            //在Starting事件中,我们已经初始化了loggingSystem
            //我们使用的是LogbackLoggingSystem
            this.loggingSystem = LoggingSystem
                    .get(event.getSpringApplication().getClassLoader());
        }
        initialize(event.getEnvironment(), event.getSpringApplication().getClassLoader());
    }
    //初始化日志
    protected void initialize(ConfigurableEnvironment environment,
        ClassLoader classLoader) {
        //应用环境属性
        new LoggingSystemProperties(environment).apply();
        LogFile logFile = LogFile.get(environment);
        if (logFile != null) {
            logFile.applyToSystemProperties();
        }
        //应用debug/trace参数
        initializeEarlyLoggingLevel(environment);
        //加载日志配置文件,应用环境属性
        initializeSystem(environment, this.loggingSystem, logFile);
        //根据环境属性设置日志输出级别
        initializeFinalLoggingLevels(environment, this.loggingSystem);
        //注册shutdown处理方式
        registerShutdownHookIfNecessary(environment, this.loggingSystem);
    }
}
4.4 ClasspathLoggingApplicationListener

打印debug日志, 记录当前classpath

public final class ClasspathLoggingApplicationListener
        implements GenericApplicationListener {

    private static final int ORDER = LoggingApplicationListener.DEFAULT_ORDER + 1;

    private static final Log logger = LogFactory
            .getLog(ClasspathLoggingApplicationListener.class);

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (logger.isDebugEnabled()) {
            if (event instanceof ApplicationEnvironmentPreparedEvent) {
                //处理EnvironmentPrepared事件
                //debug记录当前classpath
                logger.debug("Application started with classpath: " + getClasspath());
            }
            else if (event instanceof ApplicationFailedEvent) {
                logger.debug(
                        "Application failed to start with classpath: " + getClasspath());
            }
        }
    }
    
    private String getClasspath() {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        if (classLoader instanceof URLClassLoader) {
            //获取classLoader加载类和资源的搜索路径
            return Arrays.toString(((URLClassLoader) classLoader).getURLs());
        }
        return "unknown";
    }
}
4.5 BackgroundPreinitializer

扩展点, 目前只关注Starting, Ready和Failed事件, EnvironmentPrepared事件不做处理

4.6 DelegatingApplicationListener 委托监听器

扩展点, 将当前的EnvironmentPrepared事件, 广播给其他关注该事件的Environment监听器, 目前没有做任何操作

public class DelegatingApplicationListener
        implements ApplicationListener<ApplicationEvent>, Ordered {
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationEnvironmentPreparedEvent) {
            List<ApplicationListener<ApplicationEvent>> delegates = getListeners(
                    ((ApplicationEnvironmentPreparedEvent) event).getEnvironment());
            if (delegates.isEmpty()) {
                //如果delegates为空,则立即返回
                return;
            }
            this.multicaster = new SimpleApplicationEventMulticaster();
            for (ApplicationListener<ApplicationEvent> listener : delegates) {
                this.multicaster.addApplicationListener(listener);
            }
        }
        if (this.multicaster != null) {
            this.multicaster.multicastEvent(event);
        }
    }
}
4.7 FileEncodingApplicationListener

如果指定了spring.mandatory-file-encoding属性, 如果系统属性file.encoding和spring.mandatory-file-encoding不一致的话, 抛出异常

public class FileEncodingApplicationListener
        implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {

    @Override
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
        ConfigurableEnvironment environment = event.getEnvironment();
        if (!environment.containsProperty("spring.mandatory-file-encoding")) {
            //如果环境不包含spring.mandatory-file-encoding属性
            //则立即返回
            return;
        }
        //获取系统指定编码
        String encoding = System.getProperty("file.encoding");
        String desired = environment.getProperty("spring.mandatory-file-encoding");
        if (encoding != null && !desired.equalsIgnoreCase(encoding)) {
            //系统编码和指定编码不一致,那么报错
            logger.error("System property 'file.encoding' is currently '" + encoding
                    + "'. It should be '" + desired
                    + "' (as defined in 'spring.mandatoryFileEncoding').");
            logger.error("Environment variable LANG is '" + System.getenv("LANG")
                    + "'. You could use a locale setting that matches encoding='"
                    + desired + "'.");
            logger.error("Environment variable LC_ALL is '" + System.getenv("LC_ALL")
                    + "'. You could use a locale setting that matches encoding='"
                    + desired + "'.");
            throw new IllegalStateException(
                    "The Java Virtual Machine has not been configured to use the "
                            + "desired default character encoding (" + desired + ").");
        }
    }
}

5. bindToSpringApplication绑定环境

//如果指定了main函数,那么会将当前环境绑定到指定的SpringApplication中
Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));

6. 环境转换EnvironmentConverter

如果没有调用了setEnvironment方法设置了环境, 那么将自定义environment转换为StandardEnvironment

final class EnvironmentConverter {
    //环境转换
    StandardEnvironment convertEnvironmentIfNecessary(ConfigurableEnvironment environment,
            Class<? extends StandardEnvironment> type) {
        if (type.equals(environment.getClass())) {
            return (StandardEnvironment) environment;
        }
        //environment.getClass()不是StandardEnvironment的实例
        return convertEnvironment(environment, type);
    }
    
    //环境转换
    private StandardEnvironment convertEnvironment(ConfigurableEnvironment environment,
            Class<? extends StandardEnvironment> type) {
        //新建一个StandardEnvironment实例
        //然后赋值
        StandardEnvironment result = createEnvironment(type);
        result.setActiveProfiles(environment.getActiveProfiles());
        result.setConversionService(environment.getConversionService());
        copyPropertySources(environment, result);
        return result;
    }
}

7. ConfigurationPropertySources.attach(environment)

如果配置了configurationProperties属性, 那么将其放在environment的propertySources的首部

public final class ConfigurationPropertySources {

    //配置属性
    private static final String ATTACHED_PROPERTY_SOURCE_NAME = "configurationProperties";

    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转换为SpringConfigurationPropertySources,放在首位
            sources.addFirst(new ConfigurationPropertySourcesPropertySource(
                    ATTACHED_PROPERTY_SOURCE_NAME,
                    new SpringConfigurationPropertySources(sources)));
        }
    }
}

8.总结

  1. 调用getOrCreateEnvironment()方法
    • 创建了StandardServletEnvironment的实例
    • 在AbstractEnvironment的propertySources中按顺序添加了名称为servletConfigInitParams, servletContextInitParams, jndiProperties, systemProperties, systemEnvironment的属性
  2. 调用configurePropertySources()方法
    • 在AbstractEnvironment的propertySources末尾添加defaultProperties属性配置
    • 添加commandLineArgs,到propertySources首部
    • 获取spring.profiles.active属性
  3. 发布ApplicationEnvironmentPreparedEvent事件, 支持该事件的监听器有
    • ConfigFileApplicationListener获取了四个EnvironmentPostProcessor, 分别执行他们的postProcessEnvironment方法
      • SystemEnvironmentPropertySourceEnvironmentPostProcessor 将systemEnvironment属性的类型替换为SystemEnvironmentPropertySource
      • SpringApplicationJsonEnvironmentPostProcessor, 添加spring.application.json属性到jndiProperties或者systemProperties之前
      • CloudFoundryVcapEnvironmentPostProcessor 添加vcap属性到commandLineArgs之后
      • ConfigFileApplicationListener, 首先添加random属性到systemEnvironment之后, 再读取配置文件, 加载到defaultProperties属性之前
    • AnsiOutputApplicationListener 配置控制台打印字符串的颜色
    • LoggingApplicationListener 初始化日志配置
    • ClasspathLoggingApplicationListener debug打印classpath日志
    • BackgroundPreinitializer 没有做任何操作
    • DelegatingApplicationListener 没有做任何操作
    • FileEncodingApplicationListener 判断file.encoding和spring.mandatory-file-encoding是否一致
  4. 将当前环境绑定到spring.main配置的main函数中
  5. 如果设置好了自定义environment, 那么将其转换为StandardEnvironment
  6. 如果配置了configurationProperties属性, 那么将其放在environment的propertySources的首部
  7. environment的propertySources中属性列表顺序为configurationProperties, commandLineArgs, vcap, servletConfigInitParams, servletContextInitParams, spring.application.json, jndiProperties, systemProperties, systemEnvironment, random, 配置文件, defaultProperties

下一篇

我们将会在下一篇printBanner()打印Banner, 研究如何打印Banner

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容