Springboot启动流程(三):environment

Springboot启动流程(三)
之前我们研究了springboot启动流程中的listener的运行流程,接下来就该是各种环境的出事话包括environment和context这两个非常重要的spring环境
首先还是run方法的代码:
run方法代码如下:
public ConfigurableApplicationContext run(String... args) {
    // listener的流程
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                args);
        ConfigurableEnvironment environment = prepareEnvironment(listeners,
                applicationArguments);
        configureIgnoreBeanInfo(environment);
        Banner printedBanner = printBanner(environment);
        context = createApplicationContext();
        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;
}
由于listener的流程已经研究过了代码就坐了省略
接下来Environment的流程:
首先先包装了一个DefaultApplicationArguments把我们启动的参数放入类中.
然后prepareEnvironment方法准备了一个Environment
configureIgnoreBeanInfo放入需要ignore的bean
printBanner打印Banner(SpringBoot启动时打印的logo)
1.DefaultApplicationArguments 类:
这个类包装了我们启动的main函数上面的String[] args,在我们启动jar包的时候我们可以给args赋值,这个值我们同样传到后面的run方法中并做了
一系列的操作
具体代码:
public DefaultApplicationArguments(String[] args) {
    Assert.notNull(args, "Args must not be null");
    this.source = new Source(args);
    this.args = args;
}
先new了一个Source的对象,并传入args,
Source 类是DefaultApplicationArguments的内部类继承了 SimpleCommandLinePropertySource 类构造方法直接调用了super(args);所以我们只
需要看 SimpleCommandLinePropertySource 类的处理流程就行了:
SimpleCommandLinePropertySource 类构造方法:
public SimpleCommandLinePropertySource(String... args) {
    super(new SimpleCommandLineArgsParser().parse(args));
}
super 类的构造方法同样调用了父类 CommandLinePropertySource 的构造方法,并传入处理之后的对象,我们来看下new SimpleCommandLineArgsParser().parse(args)
是如何处理args的:
SimpleCommandLineArgsParser 类:
public CommandLineArgs parse(String... args) {
    CommandLineArgs commandLineArgs = new CommandLineArgs();
    for (String arg : args) {
        if (arg.startsWith("--")) {
            String optionText = arg.substring(2, arg.length());
            String optionName;
            String optionValue = null;
            if (optionText.contains("=")) {
                optionName = optionText.substring(0, optionText.indexOf('='));
                optionValue = optionText.substring(optionText.indexOf('=')+1, optionText.length());
            }
            else {
                optionName = optionText;
            }
            if (optionName.isEmpty() || (optionValue != null && optionValue.isEmpty())) {
                throw new IllegalArgumentException("Invalid argument syntax: " + arg);
            }
            commandLineArgs.addOptionArg(optionName, optionValue);
        }
        else {
            commandLineArgs.addNonOptionArg(arg);
        }
    }
    return commandLineArgs;
}
首先创建了 CommandLineArgs 对象,这个对象主要是为了存储我们启动jar包是传入的配置如:--spring.profiles.active=dev 这些与spring有关的配置
并出入key,value键值对.代码非常简单,如果已--开头则进入处理拿到name和value,并放入commandLineArgs中并放回 CommandLineArgs 对象.之后调用父类
CommandLinePropertySource 的构造方法:
public CommandLinePropertySource(T source) {
    super(COMMAND_LINE_PROPERTY_SOURCE_NAME, source);
}
其中COMMAND_LINE_PROPERTY_SOURCE_NAME是上面写死的成员变量 public static final String COMMAND_LINE_PROPERTY_SOURCE_NAME = "commandLineArgs";
然后还是调用父类的构造方法 EnumerablePropertySource,然后直接一个透传接着调用 PropertySource 的构造方法:
public PropertySource(String name, T source) {
    Assert.hasText(name, "Property source name must contain at least one character");
    Assert.notNull(source, "Property source must not be null");
    this.name = name;
    this.source = source;
}
到这儿终于调到处理的方法了:
给name和source赋值
获取了applicationArguments之后,调用prepareEnvironment方法,从名字看就是准备一个environment对象给之后用:
private ConfigurableEnvironment prepareEnvironment(
        SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments) {
    // Create and configure the environment
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    listeners.environmentPrepared(environment);
    bindToSpringApplication(environment);
    if (!this.isCustomEnvironment) {
        environment = new EnvironmentConverter(getClassLoader())
                .convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
    }
    ConfigurationPropertySources.attach(environment);
    return environment;
}
首先调用了getOrCreateEnvironment方法,如果有environment则返回,如果没有就创建:
private ConfigurableEnvironment getOrCreateEnvironment() {
    if (this.environment != null) {
        return this.environment;
    }
    switch (this.webApplicationType) {
    case SERVLET:
        return new StandardServletEnvironment();
    case REACTIVE:
        return new StandardReactiveWebEnvironment();
    default:
        return new StandardEnvironment();
    }
}
webApplicationType的赋值是在创建SpringApplication对象中调用 WebApplicationType.deduceFromClasspath() 方法实现的:
WebApplicationType 类:
static WebApplicationType deduceFromClasspath() {
    if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
            && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
            && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
        return WebApplicationType.REACTIVE;
    }
    for (String className : SERVLET_INDICATOR_CLASSES) {
        if (!ClassUtils.isPresent(className, null)) {
            return WebApplicationType.NONE;
        }
    }
    return WebApplicationType.SERVLET;
}
由于ClassLoader都是空,所以直接返回一个WebApplicationType.SERVLET,然后由getOrCreateEnvironment方法创建一个 StandardServletEnvironment 对象
在创建 StandardServletEnvironment 时,因为没有构造函数,所以直接调用父类构造方法,而他的父类 StandardEnvironment 也没有构造方法,继续调用父类
构造方法:
public AbstractEnvironment() {
    customizePropertySources(this.propertySources);
}
调用了 customizePropertySources 方法,这个方法在 StandardEnvironment 和 StandardServletEnvironment 中都有实现:
StandardServletEnvironment:
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
    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);
}
StandardEnvironment:
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
    propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
    propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
最后的效果是里面有四个值分别是:propertySources里面有四个key:servletConfigInitParams,servletContextInitParams,
systemEnvironment,systemProperties,前两个key的value是一个object();systemProperties 的value是System.getProperties();加载了一堆系统变量


回到prepareEnvironment方法中,调用configureEnvironment方法来处理environment:
protected void configureEnvironment(ConfigurableEnvironment environment,
        String[] args) {
    if (this.addConversionService) {
        ConversionService conversionService = ApplicationConversionService
                .getSharedInstance();
        environment.setConversionService(
                (ConfigurableConversionService) conversionService);
    }
    configurePropertySources(environment, args);
    configureProfiles(environment, args);
}
this.addConversionService == true,所以会调用 ApplicationConversionService.getSharedInstance();方法:
ApplicationConversionService 类:
public static ConversionService getSharedInstance() {
    ApplicationConversionService sharedInstance = ApplicationConversionService.sharedInstance;
    if (sharedInstance == null) {
        synchronized (ApplicationConversionService.class) {
            sharedInstance = ApplicationConversionService.sharedInstance;
            if (sharedInstance == null) {
                sharedInstance = new ApplicationConversionService();
                ApplicationConversionService.sharedInstance = sharedInstance;
            }
        }
    }
    return sharedInstance;
}
他会先new 一个ApplicationConversionService当做sharedInstance并返回,所以上面的 ConversionService 的实际的对象是 ApplicationConversionService
然后把conversionService强转成 ConfigurableConversionService 并赋值给environment的setConversionService方法.因为 ConfigurableConversionService
是一个继承 ApplicationConversionService 的空接口所以可以直接强转,之后调用configurePropertySources方法继续处理

protected void configurePropertySources(ConfigurableEnvironment environment,
        String[] args) {
    MutablePropertySources sources = environment.getPropertySources();
    if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
        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)) {
            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));
        }
    }
}
environment的propertySources在构造StandardServletEnvironment是赋值,里面有四个值分别是:servletConfigInitParams,servletContextInitParams,
systemEnvironment,systemProperties,因为我们启动没有加参数,所以这个方法做任何处理,如果加入了参数会走下面的if把 COMMAND_LINE_PROPERTY_SOURCE_NAME
为key,args为value的 SimpleCommandLinePropertySource 放入composite并把composite放入sources中

我们在回到configureEnvironment中,configureProfiles方法会继续处理environment对象:
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
    environment.getActiveProfiles(); // ensure they are initialized
    // But these ones should go first (last wins in a property key clash)
    Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
    profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
    environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
调用了environment.getActiveProfiles()方法:
@Override
public String[] getActiveProfiles() {
    return StringUtils.toStringArray(doGetActiveProfiles());
}
在getActiveProfiles方法中有调用了 doGetActiveProfiles() 方法:
protected Set<String> doGetActiveProfiles() {
    synchronized (this.activeProfiles) {
        if (this.activeProfiles.isEmpty()) {
            String profiles = getProperty(ACTIVE_PROFILES_PROPERTY_NAME);
            if (StringUtils.hasText(profiles)) {
                setActiveProfiles(StringUtils.commaDelimitedListToStringArray(
                        StringUtils.trimAllWhitespace(profiles)));
            }
        }
        return this.activeProfiles;
    }
}
调用getProperty方法获取profiles:
@Override
@Nullable
public String getProperty(String key) {
    return this.propertyResolver.getProperty(key);
}
我们看下propertyResolver是什么:
private final ConfigurablePropertyResolver propertyResolver = new PropertySourcesPropertyResolver(this.propertySources);
在成员变量中直接创建了 PropertySourcesPropertyResolver 的对象,传入propertySources,这个propertySources就是我们之前创建
severlet对象是赋值的propertySources,里面有四个值,但是没有ACTIVE_PROFILES_PROPERTY_NAME,所以获取的是空.
这里有个问题,ACTIVE_PROFILES_PROPERTY_NAME==spring.profiles.active,我们在spring的配置文件中会使用这个key配置读取得环境
但是现在并没有读取出来,这个配置是在后面读取的(ConfigFileApplicationListener).
回到configureProfiles方法,在获取完profiles后,把读到的东西放入environment中configurePropertySources就处理完成了.
回到prepareEnvironment方法中继续处理,接下来被调用的是listeners.environmentPrepared(environment)方法,这个方法和前面研究的
listeners.starting()方法相似,同样是调用multicastEvent方法,不过是传入的event不同:
EventPublishingRunListener 类:
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
    this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
            this.application, this.args, environment));
}
创建了 ApplicationEnvironmentPreparedEvent 对象:
ApplicationEnvironmentPreparedEvent 类:
public ApplicationEnvironmentPreparedEvent(SpringApplication application,
        String[] args, ConfigurableEnvironment environment) {
    super(application, args);
    this.environment = environment;
}
先调用了父类(SpringApplicationEvent)构造方法,然后把environment赋值给成员变量.
SpringApplicationEvent 类:
public SpringApplicationEvent(SpringApplication application, String[] args) {
    super(application);
    this.args = args;
}
调用父类(ApplicationEvent)构造方法,然后把args赋值.
ApplicationEvent 类:
public ApplicationEvent(Object source) {
    super(source);
    this.timestamp = System.currentTimeMillis();
}
同样调用了父类(EventObject)构造方法,然后赋值时间戳
EventObject 类:
public EventObject(Object source) {
    if (source == null)
        throw new IllegalArgumentException("null source");

    this.source = source;
}
赋值了source,同样的source是 SpringApplication 对象,同样的调用multicastEvent方法,同样获取Executor,同样invoke,就不在赘述了
值得注意的是,这里执行的listener是为environment赋值使用的,例如前面我们提到的读取spring.profiles.active配置,就是在
ConfigFileApplicationListener 中实现的:
在调用listener时会直接调用 ConfigFileApplicationListener 的onApplicationEvent方法
ConfigFileApplicationListener 类:
@Override
public void onApplicationEvent(ApplicationEvent event) {
    if (event instanceof ApplicationEnvironmentPreparedEvent) {
        onApplicationEnvironmentPreparedEvent(
                (ApplicationEnvironmentPreparedEvent) event);
    }
    if (event instanceof ApplicationPreparedEvent) {
        onApplicationPreparedEvent(event);
    }
}
显然event就是ApplicationEnvironmentPreparedEvent,所以会进入
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event)方法
private void onApplicationEnvironmentPreparedEvent(
            ApplicationEnvironmentPreparedEvent event) {
    List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
    postProcessors.add(this);
    AnnotationAwareOrderComparator.sort(postProcessors);
    for (EnvironmentPostProcessor postProcessor : postProcessors) {
        postProcessor.postProcessEnvironment(event.getEnvironment(),
                event.getSpringApplication());
    }
}
方法首先调用loadPostProcessors方法做一些处理:
List<EnvironmentPostProcessor> loadPostProcessors() {
    return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class,
            getClass().getClassLoader());
}
调用了 SpringFactoriesLoader 的loadFactories方法,这个方法与之前对的loadFactoriesNames相似,不同的是方法返回的是具体对象:
SpringFactoriesLoader 类:
public static <T> List<T> loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader) {
    Assert.notNull(factoryClass, "'factoryClass' must not be null");
    ClassLoader classLoaderToUse = classLoader;
    if (classLoaderToUse == null) {
        classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
    }
    List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
    if (logger.isTraceEnabled()) {
        logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);
    }
    List<T> result = new ArrayList<>(factoryNames.size());
    for (String factoryName : factoryNames) {
        result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
    }
    AnnotationAwareOrderComparator.sort(result);
    return result;
}
传入的classLoader是ConfigFileApplicationListener的classLoader,然后还是调用了loadFactoryNames方法获取所有需要加载的类名
这里类名有三个:
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor
然后创建了这三个对象返回 onApplicationEnvironmentPreparedEvent 方法,
onApplicationEnvironmentPreparedEvent 接下来来的处理是在postProcessors添加了自己,所以现在postProcessors理由四个对象:
以上三个类的对象和SpringFactoriesLoader的对象.然后循环调用postProcessEnvironment方法做处理,其中
systemEnvironmentPropertySourceEnvironmentPostProcessor 的 postProcessEnvironment 主要是更新系统的配置,
springApplicationJsonEnvironmentPostProcessor 的 postProcessEnvironment 处理一些json相关的东西
cloudFoundryVcapEnvironmentPostProcessor 的 postProcessEnvironment 处理springcloud的东西对于我们都没有什么用,
但是 configFileApplicationListener 的 postProcessEnvironment 处理的东西对于我们相当重要!
ConfigFileApplicationListener 类:
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
        SpringApplication application) {
    addPropertySources(environment, application.getResourceLoader());
}
首先调用了 postProcessEnvironment 处理,这个方法直接调用 addPropertySources:
protected void addPropertySources(ConfigurableEnvironment environment,
        ResourceLoader resourceLoader) {
    RandomValuePropertySource.addToEnvironment(environment);
    new Loader(environment, resourceLoader).load();
}
从字面上看第一步加入一个什么东西到environment中,然后在load个什么东西具体加入和load的内容是什么呢?
RandomValuePropertySource 类:
public static void addToEnvironment(ConfigurableEnvironment environment) {
    environment.getPropertySources().addAfter(
            StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
            new RandomValuePropertySource(RANDOM_PROPERTY_SOURCE_NAME));
    logger.trace("RandomValuePropertySource add to Environment");
}
这个方法特别简单,创建了个RandomValuePropertySource的类然后放在environment中的propertySources里SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME
key的后面,至于RandomValuePropertySource对象的创建,不用看了特别简单就是获取一个随机数.
经过这样的处理environment中的source里加了一个对象:RandomValuePropertySource {name='random',value=随机数}
这个东西现在还不知道是干什么用的...
加完随机数,我们要继续处理load一个什么东西:
首先创建Load对象:
Loader 类:
Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
    this.environment = environment;
    this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(
            this.environment);
    this.resourceLoader = (resourceLoader != null) ? resourceLoader
            : new DefaultResourceLoader();
    this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(
            PropertySourceLoader.class, getClass().getClassLoader());
}
把environment中的属性赋值一份放在Loader里,resourceLoader就是 DefaultResourceLoader ,获取的是当前线程的classLoader
然后propertySourceLoaders还是调用loadFactories方法获取对象,获取完的对象 有两个:
PropertiesPropertySourceLoader
YamlPropertySourceLoader
一个负责处理.properties一个负责处理.yml
然后调用load方法:
ublic void load() {
    this.profiles = new LinkedList<>();
    this.processedProfiles = new LinkedList<>();
    this.activatedProfiles = false;
    this.loaded = new LinkedHashMap<>();
    initializeProfiles();
    while (!this.profiles.isEmpty()) {
        Profile profile = this.profiles.poll();
        if (profile != null && !profile.isDefaultProfile()) {
            addProfileToEnvironment(profile.getName());
        }
        load(profile, this::getPositiveProfileFilter,
                addToLoaded(MutablePropertySources::addLast, false));
        this.processedProfiles.add(profile);
    }
    resetEnvironmentProfiles(this.processedProfiles);
    load(null, this::getNegativeProfileFilter,
            addToLoaded(MutablePropertySources::addFirst, true));
    addLoadedPropertySources();
}
先调用 initializeProfiles 初始化,然后从profiles中取出profiles进行处理
private void initializeProfiles() {
    // The default profile for these purposes is represented as null. We add it
    // first so that it is processed first and has lowest priority.
    this.profiles.add(null);
    Set<Profile> activatedViaProperty = getProfilesActivatedViaProperty();
    this.profiles.addAll(getOtherActiveProfiles(activatedViaProperty));
    // Any pre-existing active profiles set via property sources (e.g.
    // System properties) take precedence over those added in config files.
    addActiveProfiles(activatedViaProperty);
    if (this.profiles.size() == 1) { // only has null profile
        for (String defaultProfileName : this.environment.getDefaultProfiles()) {
            Profile defaultProfile = new Profile(defaultProfileName, true);
            this.profiles.add(defaultProfile);
        }
    }
}
这段代码首先在profiles中添加一个null,然后调用 getProfilesActivatedViaProperty
private Set<Profile> getProfilesActivatedViaProperty() {
    if (!this.environment.containsProperty(ACTIVE_PROFILES_PROPERTY)
            && !this.environment.containsProperty(INCLUDE_PROFILES_PROPERTY)) {
        return Collections.emptySet();
    }
    Binder binder = Binder.get(this.environment);
    Set<Profile> activeProfiles = new LinkedHashSet<>();
    activeProfiles.addAll(getProfiles(binder, INCLUDE_PROFILES_PROPERTY));
    activeProfiles.addAll(getProfiles(binder, ACTIVE_PROFILES_PROPERTY));
    return activeProfiles;
}
从environment中的resouce获取 ACTIVE_PROFILES_PROPERTY(spring.profiles.active)和INCLUDE_PROFILES_PROPERTY(spring.profiles.include)
因为environment中没有所以直接返回一个空的set集合.
回到初始化方法initializeProfiles中,在获取activatedViaProperty后有调用this.profiles.addAll(getOtherActiveProfiles(activatedViaProperty))
方法把activatedViaProperty先处理然后放入profiles中:
private List<Profile> getOtherActiveProfiles(Set<Profile> activatedViaProperty) {
    return Arrays.stream(this.environment.getActiveProfiles()).map(Profile::new)
            .filter((profile) -> !activatedViaProperty.contains(profile))
            .collect(Collectors.toList());
}
调用了environment.getActiveProfiles()方法获取,这个方法在之前研究过,获取不到任何东西,所以返回依旧是null,这些操作之后profiles内的
对象依然只有一个null,之后调到了addActiveProfiles(activatedViaProperty)方法:
void addActiveProfiles(Set<Profile> profiles) {
    if (profiles.isEmpty()) {
        return;
    }
    if (this.activatedProfiles) {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Profiles already activated, '" + profiles
                    + "' will not be applied");
        }
        return;
    }
    this.profiles.addAll(profiles);
    if (this.logger.isDebugEnabled()) {
        this.logger.debug("Activated activeProfiles "
                + StringUtils.collectionToCommaDelimitedString(profiles));
    }
    this.activatedProfiles = true;
    removeUnprocessedDefaultProfiles();
}
因为profiles是empty的,所以直接返回,,没有记日志.
之后判断profiles的size是否等于1,进入for循环处理
for (String defaultProfileName : this.environment.getDefaultProfiles()) {
    Profile defaultProfile = new Profile(defaultProfileName, true);
    this.profiles.add(defaultProfile);
}
首先调用了environment.getDefaultProfiles()方法:
@Override
public String[] getDefaultProfiles() {
    return StringUtils.toStringArray(doGetDefaultProfiles());
}
protected Set<String> doGetDefaultProfiles() {
    synchronized (this.defaultProfiles) {
        if (this.defaultProfiles.equals(getReservedDefaultProfiles())) {
            String profiles = getProperty(DEFAULT_PROFILES_PROPERTY_NAME);
            if (StringUtils.hasText(profiles)) {
                setDefaultProfiles(StringUtils.commaDelimitedListToStringArray(
                        StringUtils.trimAllWhitespace(profiles)));
            }
        }
        return this.defaultProfiles;
    }
}
protected Set<String> getReservedDefaultProfiles() {
    return Collections.singleton(RESERVED_DEFAULT_PROFILE_NAME);
}
doGetDefaultProfiles首先判断了this.defaultProfiles.equals(getReservedDefaultProfiles(),this.defaultProfiles在成员变量里直接赋值了:
private final Set<String> defaultProfiles = new LinkedHashSet<>(getReservedDefaultProfiles());
所以肯定是equals的,然后调用getProperty方法直接返回了environment中的propertyResolver中的spring.profiles.default为key
的value值,返回的依旧是空,然后方法直接返回defaultProfiles即default,然后for循环会创建Profile对象:
Profile 类:
Profile(String name, boolean defaultProfile) {
    Assert.notNull(name, "Name must not be null");
    this.name = name;
    this.defaultProfile = defaultProfile;
}
把defaultProfileName储存起来,然后add加入defaultProfile对象并返回完成初始化流程.
随后load方法会取出profile进行处理,调用load方法:
load(profile, this::getPositiveProfileFilter,addToLoaded(MutablePropertySources::addLast, false));
private void load(Profile profile, DocumentFilterFactory filterFactory,
        DocumentConsumer consumer) {
    getSearchLocations().forEach((location) -> {
        boolean isFolder = location.endsWith("/");
        Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
        names.forEach(
                (name) -> load(location, name, profile, filterFactory, consumer));
    });
}
首先getSearchLocations获取所有前缀
private Set<String> getSearchLocations() {
    if (this.environment.containsProperty(CONFIG_LOCATION_PROPERTY)) {
        return getSearchLocations(CONFIG_LOCATION_PROPERTY);
    }
    Set<String> locations = getSearchLocations(
            CONFIG_ADDITIONAL_LOCATION_PROPERTY);
    locations.addAll(
            asResolvedSet(ConfigFileApplicationListener.this.searchLocations,
                    DEFAULT_SEARCH_LOCATIONS));
    return locations;
}

private Set<String> getSearchLocations(String propertyName) {
    Set<String> locations = new LinkedHashSet<>();
    if (this.environment.containsProperty(propertyName)) {
        for (String path : asResolvedSet(
                this.environment.getProperty(propertyName), null)) {
            if (!path.contains("$")) {
                path = StringUtils.cleanPath(path);
                if (!ResourceUtils.isUrl(path)) {
                    path = ResourceUtils.FILE_URL_PREFIX + path;
                }
            }
            locations.add(path);
        }
    }
    return locations;
}
一串代码下来获取的是DEFAULT_SEARCH_LOCATIONS("classpath:/,classpath:/config/,file:./,file:./config/"),在我们之后读取配置文件时
这些前缀会帮助我们拼接地址
然后获取DEFAULT_NAMES(application),然后循环调用load
private void load(String location, String name, Profile profile,
        DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
    if (!StringUtils.hasText(name)) {
        for (PropertySourceLoader loader : this.propertySourceLoaders) {
            if (canLoadFileExtension(loader, location)) {
                load(loader, location, profile,
                        filterFactory.getDocumentFilter(profile), consumer);
                return;
            }
        }
    }
    Set<String> processed = new HashSet<>();
    for (PropertySourceLoader loader : this.propertySourceLoaders) {
        for (String fileExtension : loader.getFileExtensions()) {
            if (processed.add(fileExtension)) {
                loadForFileExtension(loader, location + name, "." + fileExtension,
                        profile, filterFactory, consumer);
            }
        }
    }
}
因为StringUtils.hasText(name)返回true,走下面的代码:
Set<String> processed = new HashSet<>();
for (PropertySourceLoader loader : this.propertySourceLoaders) {
    for (String fileExtension : loader.getFileExtensions()) {
        if (processed.add(fileExtension)) {
            loadForFileExtension(loader, location + name, "." + fileExtension,
                    profile, filterFactory, consumer);
        }
    }
}
获取propertySourceLoaders一个个处理,propertySourceLoaders就是之前获取的yml和properties的loader
for循环中获取loader.getFileExtensions()返回{ "properties", "xml" }和{ "yml", "yaml" },然后调用loadForFileExtension
方法:
private void loadForFileExtension(PropertySourceLoader loader, String prefix,
        String fileExtension, Profile profile,
        DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
    DocumentFilter defaultFilter = filterFactory.getDocumentFilter(null);
    DocumentFilter profileFilter = filterFactory.getDocumentFilter(profile);
    if (profile != null) {
        // Try profile-specific file & profile section in profile file (gh-340)
        String profileSpecificFile = prefix + "-" + profile + fileExtension;
        load(loader, profileSpecificFile, profile, defaultFilter, consumer);
        load(loader, profileSpecificFile, profile, profileFilter, consumer);
        // Try profile specific sections in files we've already processed
        for (Profile processedProfile : this.processedProfiles) {
            if (processedProfile != null) {
                String previouslyLoaded = prefix + "-" + processedProfile
                        + fileExtension;
                load(loader, previouslyLoaded, profile, profileFilter, consumer);
            }
        }
    }
    // Also try the profile-specific section (if any) of the normal file
    // 加载其他application-test,application-prod...没有用到的配置文件
    load(loader, prefix + fileExtension, profile, profileFilter, consumer);
}
private void load(PropertySourceLoader loader, String location, Profile profile,
                DocumentFilter filter, DocumentConsumer consumer) {
    try {
        Resource resource = this.resourceLoader.getResource(location);
        if (resource == null || !resource.exists()) {
            if (this.logger.isTraceEnabled()) {
                StringBuilder description = getDescription(
                        "Skipped missing config ", location, resource, profile);
                this.logger.trace(description);
            }
            return;
        }
        if (!StringUtils.hasText(
                StringUtils.getFilenameExtension(resource.getFilename()))) {
            if (this.logger.isTraceEnabled()) {
                StringBuilder description = getDescription(
                        "Skipped empty config extension ", location, resource,
                        profile);
                this.logger.trace(description);
            }
            return;
        }
        String name = "applicationConfig: [" + location + "]";
        List<Document> documents = loadDocuments(loader, name, resource);
        if (CollectionUtils.isEmpty(documents)) {
            if (this.logger.isTraceEnabled()) {
                StringBuilder description = getDescription(
                        "Skipped unloaded config ", location, resource, profile);
                this.logger.trace(description);
            }
            return;
        }
        List<Document> loaded = new ArrayList<>();
        for (Document document : documents) {
            if (filter.match(document)) {
                addActiveProfiles(document.getActiveProfiles());
                addIncludedProfiles(document.getIncludeProfiles());
                loaded.add(document);
            }
        }
        Collections.reverse(loaded);
        if (!loaded.isEmpty()) {
            loaded.forEach((document) -> consumer.accept(profile, document));
            if (this.logger.isDebugEnabled()) {
                StringBuilder description = getDescription("Loaded config file ",
                        location, resource, profile);
                this.logger.debug(description);
            }
        }
    }
    catch (Exception ex) {
        throw new IllegalStateException("Failed to load property "
                + "source from location '" + location + "'", ex);
    }
}
只有在拼接成classpath:application.properties时才能真正加载上面的代码:
会走这一段代码:
String name = "applicationConfig: [" + location + "]";
List<Document> documents = loadDocuments(loader, name, resource);
if (CollectionUtils.isEmpty(documents)) {
    if (this.logger.isTraceEnabled()) {
        StringBuilder description = getDescription(
                "Skipped unloaded config ", location, resource, profile);
        this.logger.trace(description);
    }
    return;
}
List<Document> loaded = new ArrayList<>();
for (Document document : documents) {
    if (filter.match(document)) {
        addActiveProfiles(document.getActiveProfiles());
        addIncludedProfiles(document.getIncludeProfiles());
        loaded.add(document);
    }
}
Collections.reverse(loaded);
if (!loaded.isEmpty()) {
    loaded.forEach((document) -> consumer.accept(profile, document));
    if (this.logger.isDebugEnabled()) {
        StringBuilder description = getDescription("Loaded config file ",
                location, resource, profile);
        this.logger.debug(description);
    }
}
调用loadDocuments方法加载配置文件:
private List<Document> loadDocuments(PropertySourceLoader loader, String name,
        Resource resource) throws IOException {
    DocumentsCacheKey cacheKey = new DocumentsCacheKey(loader, resource);
    List<Document> documents = this.loadDocumentsCache.get(cacheKey);
    if (documents == null) {
        List<PropertySource<?>> loaded = loader.load(name, resource);
        documents = asDocuments(loaded);
        this.loadDocumentsCache.put(cacheKey, documents);
    }
    return documents;
}
先从cache中获取documents,如果没有就loader.load(name, resource)读取,这是初始化肯定在cache中没有数据
则调用loader.load:
@Override
public List<PropertySource<?>> load(String name, Resource resource)
        throws IOException {
    Map<String, ?> properties = loadProperties(resource);
    if (properties.isEmpty()) {
        return Collections.emptyList();
    }
    return Collections
            .singletonList(new OriginTrackedMapPropertySource(name, properties));
}

@SuppressWarnings({ "unchecked", "rawtypes" })
private Map<String, ?> loadProperties(Resource resource) throws IOException {
    String filename = resource.getFilename();
    if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) {
        return (Map) PropertiesLoaderUtils.loadProperties(resource);
    }
    return new OriginTrackedPropertiesLoader(resource).load();
}
直接使用PropertiesLoaderUtils获取了properties中所有的配置信息然后转换成document对象返回
返回到load方法继续执行
for (Document document : documents) {
    if (filter.match(document)) {
        addActiveProfiles(document.getActiveProfiles());
        addIncludedProfiles(document.getIncludeProfiles());
        loaded.add(document);
    }
}
直接循环调用addActiveProfiles和addIncludedProfiles赋值了profiles和 include
返回最初的load:
再次循环发现第二次取回取出配置文件中填写的spring.profiles.active=xxx 中的xxx
然后接着load和loadForFileExtension
while (!this.profiles.isEmpty()) {
    Profile profile = this.profiles.poll();
    if (profile != null && !profile.isDefaultProfile()) {
        addProfileToEnvironment(profile.getName());
    }
    load(profile, this::getPositiveProfileFilter,
            addToLoaded(MutablePropertySources::addLast, false));
    this.processedProfiles.add(profile);
}

private void addProfileToEnvironment(String profile) {
    for (String activeProfile : this.environment.getActiveProfiles()) {
        if (activeProfile.equals(profile)) {
            return;
        }
    }
    this.environment.addActiveProfile(profile);
}
因为现在environment中还没有activeProfile,所以调用this.environment.addActiveProfile(profile);方法把profiles放入environment中:
AbstractEnvironment 类:
@Override
public void addActiveProfile(String profile) {
    if (logger.isDebugEnabled()) {
        logger.debug("Activating profile '" + profile + "'");
    }
    validateProfile(profile);
    doGetActiveProfiles();
    synchronized (this.activeProfiles) {
        this.activeProfiles.add(profile);
    }
}
调用 doGetActiveProfiles 方法,前面知道返回空,然后把profile加入profiles中,
private void loadForFileExtension(PropertySourceLoader loader, String prefix,
    String fileExtension, Profile profile,
    DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
DocumentFilter defaultFilter = filterFactory.getDocumentFilter(null);
DocumentFilter profileFilter = filterFactory.getDocumentFilter(profile);
if (profile != null) {
    // Try profile-specific file & profile section in profile file (gh-340)
    String profileSpecificFile = prefix + "-" + profile + fileExtension;
    load(loader, profileSpecificFile, profile, defaultFilter, consumer);
    load(loader, profileSpecificFile, profile, profileFilter, consumer);
    // Try profile specific sections in files we've already processed
    for (Profile processedProfile : this.processedProfiles) {
        if (processedProfile != null) {
            String previouslyLoaded = prefix + "-" + processedProfile
                    + fileExtension;
            load(loader, previouslyLoaded, profile, profileFilter, consumer);
        }
    }
}
// Also try the profile-specific section (if any) of the normal file
load(loader, prefix + fileExtension, profile, profileFilter, consumer);
}
这一次传入的profile不为空,所以会走loadForFileExtension的
if (profile != null) {
    // Try profile-specific file & profile section in profile file (gh-340)
    String profileSpecificFile = prefix + "-" + profile + fileExtension;
    load(loader, profileSpecificFile, profile, defaultFilter, consumer);
    load(loader, profileSpecificFile, profile, profileFilter, consumer);
    // Try profile specific sections in files we've already processed
    for (Profile processedProfile : this.processedProfiles) {
        if (processedProfile != null) {
            String previouslyLoaded = prefix + "-" + processedProfile
                    + fileExtension;
            load(loader, previouslyLoaded, profile, profileFilter, consumer);
        }
    }
}
一段代码:
再次load配置文件,加载application-xxx.properties的文件,之后推出循环运行:
resetEnvironmentProfiles(this.processedProfiles);// 重新set一遍processedProfiles
load(null, this::getNegativeProfileFilter,
                    addToLoaded(MutablePropertySources::addFirst, true));// 再次加载
addLoadedPropertySources();

private void addLoadedPropertySources() {
    MutablePropertySources destination = this.environment.getPropertySources();
    List<MutablePropertySources> loaded = new ArrayList<>(this.loaded.values());
    Collections.reverse(loaded);
    String lastAdded = null;
    Set<String> added = new HashSet<>();
    for (MutablePropertySources sources : loaded) {
        for (PropertySource<?> source : sources) {
            if (added.add(source.getName())) {
                addLoadedPropertySource(destination, lastAdded, source);
                lastAdded = source.getName();
            }
        }
    }
}
这段代码把loaded中的变量放入environment.getPropertySources()中,放入了两个:application-dvep.properties和 application.properties
现在environment.getPropertySources中一共有7个对象了
代码执行到这里,整个 ConfigFileApplicationListener 类已经全部完成加载,现在我们总结一下这个监听器都做了什么:
总的来说就是加载配置文件.
但是健在的流程是获取所有的loader包括properties和yml的loader,然后在加载default的配置文件名获得application和default的配置文件路径获得
classpath:/,classpath:/config/,file:./,file:./config/ 文件路径,遍历拼装配置文件路径并获取配置文件里的内容,把他们放入environment中
这点儿的代码稍微有点儿复杂,接下来我们要看的是bind的操作.
现在的问题:
配置文件中的${xxx}在什么时候会被替换,现在我暂时没有找到...

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值