04、SpringBoot 启动 准备运行环境(prepareEnvironment)流程(一)

目录Springboot源码学习目录
上文03、SpringBoot 启动 执行SpringApplication的run方法 准备运行环境前 流程
前言

一、准备运行环境方法(prepareEnvironment)

    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
		DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
	// 获取配置环境对象 见下文标题二
	ConfigurableEnvironment environment = getOrCreateEnvironment();
        // 对配置环境对象进行相应属性初始化 见下文标题三
	configureEnvironment(environment, applicationArguments.getSourceArgs());
        // 检查环境对象中有没有 名字为 configurationProperties 的配置源,如果有就删除掉,然后创建一个放在第一个位置,见下文标题四
	ConfigurationPropertySources.attach(environment);
        // 发送环境准备完成事件(environmentPrepared),对环境配置环境对象进行增强
        // 其中主要的就是EnvironmentPostProcessorApplicationListener监听器,配置文件的加载和解析都在这个监听器中完成
        // 该步也是准备运行环境的核心步骤,其他步骤都可以不看,无关紧要,但该步是最重要的,我们放到下一篇文章讲解
	listeners.environmentPrepared(bootstrapContext, environment);
        // 移动名称为defaultProperties的属性源到最后面,使其成为配置文件对象中的最后一个源,见下文标题五
	DefaultPropertiesPropertySource.moveToEnd(environment);
        // 将环境对象绑定到当前Spring应用(SpringApplication),见下文标题六
	bindToSpringApplication(environment);
        // 判断是否为定制环境,如果是,将该环境对象转换为StandardEnvironment类型
        // 默认情况下不是定制环境(isCustomEnvironment = false),一般走到该步骤环境对象为StandardServletEnvironment类型
	if (!this.isCustomEnvironment) {
		environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
				deduceEnvironmentClass());
	}
        // 再次检查环境对象中有没有 名字为 configurationProperties 的配置源,如果有就删除掉,然后创建一个放在第一个位置,见下文标题四
	ConfigurationPropertySources.attach(environment);
	return environment;
    }

二、获取配置环境对象

    private ConfigurableEnvironment getOrCreateEnvironment() {
        // 判断当前Spring应用中有没有环境属性,如果有直接返回,我们这是第一次启东应用,此时是没有的
        if (this.environment != null) {
            return this.environment;
        }
        // 前面实例化SpringApplication是,推断并初始化了webApplicationType属性,值为SERVLET
        switch (this.webApplicationType) {
            case SERVLET:
                return new StandardServletEnvironment();
            case REACTIVE:
                return new StandardReactiveWebEnvironment();
            default:
                return new StandardEnvironment();
        }
    }

三、对环境对象进行赋值

在该步骤之直接我们先看一下debug截图中环境对象有哪些属性源

一个servlet配置属性源,一个servlet上下文属性源,一个操作系统环境变量属性源,一个jvm环境变量属性源
然后我们开始执行

    protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
        // environment向环境对象中设置一个转换器
	if (this.addConversionService) {
		ConversionService conversionService = ApplicationConversionService.getSharedInstance();
		environment.setConversionService((ConfigurableConversionService) conversionService);
	}
        // 设置属性源,该步就是将程序启动时的入参包装成一个属性源,并添加到环境中
	configurePropertySources(environment, args);
        // 设置环境,空实现
	configureProfiles(environment, args);
    }

继续debug进入上一步中的configurePropertySources方法

    protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
        // 获取当前环境中的属性源集合对象
	MutablePropertySources sources = environment.getPropertySources();
        // 如果默认的属性源不为空,则将他合并到 属性源集合对象
	if (!CollectionUtils.isEmpty(this.defaultProperties)) {
		DefaultPropertiesPropertySource.addOrMerge(this.defaultProperties, sources);
	}
        // 判断当前SpringApplication对象是否运行添加命令,以及启动入参是否部位空
	if (this.addCommandLineProperties && args.length > 0) {
                // 判断属性源中是否有名字为commandLineArgs的属性源
		String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
		if (sources.contains(name)) {
                        // 如果有将创建一个名字为commandLineArgs的组合属性源CompositePropertySource
			PropertySource<?> source = sources.get(name);
			CompositePropertySource composite = new CompositePropertySource(name);
                        // // 然后将入参的包装成名字为springApplicationCommandLineArgs的SimpleCommandLinePropertySource类型属性源,加入组合属性源
			composite.addPropertySource(
					new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
                        // 再将原来的属性源也加入组合属性源
			composite.addPropertySource(source);
                        // 再将属性源集合对象中名字为commandLineArgs的属性源替换为组合属性源
			sources.replace(name, composite);
		}
		else {  
                        // 如果没有直接将入参包装成SimpleCommandLinePropertySource类型的属性源,并存入第一个位置,名字默认为commandLineArgs
			sources.addFirst(new SimpleCommandLinePropertySource(args));
		}
	}
    }

在该步骤之后我们看一下环境中的属性源有哪些

可以看到和执行完该步骤之前比在第一个位置多了一个属性源,名字就是commandLineArgs,并且里面的key-value正式我们启动是加入的参数

四、添加configurationProperties属性源

往环境的属性源集合中增加一个名字为configurationProperties的属性源,但是我其实并没有看明白这个属性的作用是什么
仅仅是看看他的源码中的逻辑

    public static void attach(Environment environment) {
	Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
	MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
        // 从属性源集合中获取名字为configurationProperties的属性源,如果有则删除掉
	PropertySource<?> attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME);
	if (attached != null && attached.getSource() != sources) {
		sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
		attached = null;
	}
        // 因为前面的操作,到该步骤,属性源肯定为null,然后创建一个名字为configurationProperties,的ConfigurationPropertySourcesPropertySource类型属性源
        // 并且将属性源集合加入该属性源
	if (attached == null) {
		sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
				new SpringConfigurationPropertySources(sources)));
	}
    }

五、将名字为defaultProperties的属性源移动到属性源集合的最后的位置

    public static void moveToEnd(ConfigurableEnvironment environment) {
	moveToEnd(environment.getPropertySources());
    }

    public static void moveToEnd(MutablePropertySources propertySources) {
        // 获取名字为defaultProperties的属性源,如果存在则移动到最后面
	PropertySource<?> propertySource = propertySources.remove(NAME);
	if (propertySource != null) {
		propertySources.addLast(propertySource);
	}
    }

六、将环境对象SpringApplication对象绑定

这一步我也没看明白他的目的是什么,只知道,是从环境对象中的属性源中取出key为spring.main的值,然后赋值给SpringApplication对象

    protected void bindToSpringApplication(ConfigurableEnvironment environment) {
	try {
		Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));
	}
	catch (Exception ex) {
		throw new IllegalStateException("Cannot bind to SpringApplication", ex);
	}
    }
posted @ 2021-11-03 22:09  君子思  阅读(476)  评论(0)    收藏  举报