SpringBoot外部化配置

 查看SpringApplication.run方法的源代码,其源代码如下所示:

public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    ArrayList exceptionReporters = new ArrayList();
    this.configureHeadlessProperty();
    SpringApplicationRunListeners listeners = this.getRunListeners(args);
    listeners.starting();

    Collection exceptionReporters1;
    try {
        DefaultApplicationArguments ex = new DefaultApplicationArguments(args);
        //准备Spring的运行环境信息
        ConfigurableEnvironment environment = this.prepareEnvironment(listeners, ex);
        this.configureIgnoreBeanInfo(environment);
        Banner printedBanner = this.printBanner(environment);
        context = this.createApplicationContext();
        exceptionReporters1 = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, new Object[]{context});
        //准备上下文,包括设置父上下文
        this.prepareContext(context, environment, listeners, ex, printedBanner);
        this.refreshContext(context);
        this.afterRefresh(context, ex);
        stopWatch.stop();
        if(this.logStartupInfo) {
            (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
        }

        listeners.started(context);
        this.callRunners(context, ex);
    } catch (Throwable var10) {
        this.handleRunFailure(context, var10, exceptionReporters, listeners);
        throw new IllegalStateException(var10);
    }

    try {
        listeners.running(context);
        return context;
    } catch (Throwable var9) {
        this.handleRunFailure(context, var9, exceptionReporters1, (SpringApplicationRunListeners)null);
        throw new IllegalStateException(var9);
    }
}

 

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
    //根据上下文创建环境
    Object environment = this.getOrCreateEnvironment();
    this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
    ConfigurationPropertySources.attach((Environment)environment);
    listeners.environmentPrepared((ConfigurableEnvironment)environment);
    this.bindToSpringApplication((ConfigurableEnvironment)environment);
    if(!this.isCustomEnvironment) {
        environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
    }

    ConfigurationPropertySources.attach((Environment)environment);
    return (ConfigurableEnvironment)environment;
}

 

private ConfigurableEnvironment getOrCreateEnvironment() {
    if(this.environment != null) {
        return this.environment;
    } else {
        //根据webApplicationType类型创建不同的环境,webApplicationType是根据WebApplicationType.deduceFromClasspath()方法来确定的,
        switch(null.$SwitchMap$org$springframework$boot$WebApplicationType[this.webApplicationType.ordinal()]) {
        case 1:
            return new StandardServletEnvironment();//基于servlet的web server
        case 2:
            return new StandardReactiveWebEnvironment();//基于reactive的web server
        default:
            return new StandardEnvironment();
        }
    }
}

以StandardServletEnvironment的环境为例,其父类AbstractEnvironment的构造函数调用了customizePropertySources方法,StandardServletEnvironment重写了customizePropertySources方法,将servletConfigInitParams,servletContextInitParams,jndiProperties,systemProperties,systemEnvironment等PropertySource按顺序添加到propertySources集合中。

public AbstractEnvironment() {
    this.propertyResolver = new PropertySourcesPropertyResolver(this.propertySources);
    this.customizePropertySources(this.propertySources);
}
StandardServletEnvironment.customizePropertySources
protected void customizePropertySources(MutablePropertySources propertySources) {
    propertySources.addLast(new StubPropertySource("servletConfigInitParams"));
    propertySources.addLast(new StubPropertySource("servletContextInitParams"));
    if(JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
        propertySources.addLast(new JndiPropertySource("jndiProperties"));
    }
    super.customizePropertySources(propertySources);
}
StandardEnvironment.customizePropertySources
protected void customizePropertySources(MutablePropertySources propertySources) {
    propertySources.addLast(new PropertiesPropertySource("systemProperties", this.getSystemProperties()));
    propertySources.addLast(new SystemEnvironmentPropertySource("systemEnvironment", this.getSystemEnvironment()));
}
然后调用SpringApplication.configureEnvironment方法,设置defaultProperties和commandLineArgs的PropertySource到propertySources,commandLineArgs放到集合的最前面
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
    if(this.addConversionService) {
        ConversionService conversionService = ApplicationConversionService.getSharedInstance();
        environment.setConversionService((ConfigurableConversionService)conversionService);
    }

    this.configurePropertySources(environment, args);
    this.configureProfiles(environment, args);
}
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 = "commandLineArgs";
        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));
        }
    }

}

此时PropertySources的配置文件顺序如下:

    commandLineArgs

    servletConfigInitParams

    servletContextInitParams

    jndiProperties

    systemProperties

    systemEnvironment

    defaultProperties

基本的环境配置文件准备完成之后,调用SpringApplicationRunListeners.environmentPrepared方法,发布ApplicationEnvironmentPreparedEvent事件,通知对应的监听器,主要看ConfigFileApplicationListener,ConfigFileApplicationListener.onApplicationEnvironmentPreparedEvent方法监听了ApplicationEnvironmentPreparedEvent事件,ConfigFileApplicationListener即是EnvironmentPostProcessor,又是ApplicationListener。

private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
    //通过SPI机制,获得配置的所有EnvironmentPostProcessor的实现类。
    List postProcessors = this.loadPostProcessors();
    //ConfigFileApplicationListener也是EnvironmentPostProcessor的实现类,将自己加入到postProcessors 中
    postProcessors.add(this); 
    //对postProcessors 进行排序   
    AnnotationAwareOrderComparator.sort(postProcessors);
    Iterator var3 = postProcessors.iterator();

    while(var3.hasNext()) {
        EnvironmentPostProcessor postProcessor = (EnvironmentPostProcessor)var3.next();
        //调用EnvironmentPostProcessor实现类的postProcessEnvironment方法,主要查看ConfigFileApplicationListener.postProcessEnvironment方法                  
        postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
    }
}
List<EnvironmentPostProcessor> loadPostProcessors() {
    return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, this.getClass().getClassLoader());
}
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
    this.addPropertySources(environment, application.getResourceLoader());
}
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
    //将RandomValuePropertySource加入到环境的PropertySources集合中,放在systemEnvironment之后
    RandomValuePropertySource.addToEnvironment(environment);
    (new ConfigFileApplicationListener.Loader(environment, resourceLoader)).load();
}
void load() {
    FilteredPropertySource.apply(this.environment, "defaultProperties", ConfigFileApplicationListener.LOAD_FILTERED_PROPERTY, (defaultProperties) -> {
        this.profiles = new LinkedList();
        this.processedProfiles = new LinkedList();
        this.activatedProfiles = false;
        this.loaded = new LinkedHashMap();
        this.initializeProfiles();

        while(!this.profiles.isEmpty()) {
            ConfigFileApplicationListener.Profile profile = (ConfigFileApplicationListener.Profile)this.profiles.poll();
            if(this.isDefaultProfile(profile)) {
                this.addProfileToEnvironment(profile.getName());
            }
            //根据激活的profile名称,加载对应的配置文件
            this.load(profile, this::getPositiveProfileFilter, this.addToLoaded(MutablePropertySources::addLast, false));
            this.processedProfiles.add(profile);
        }

        this.load((ConfigFileApplicationListener.Profile)null, this::getNegativeProfileFilter, this.addToLoaded(MutablePropertySources::addFirst, true));
        this.addLoadedPropertySources();
        this.applyActiveProfiles(defaultProperties);
    });
}
private void load(ConfigFileApplicationListener.Profile profile, ConfigFileApplicationListener.DocumentFilterFactory filterFactory, ConfigFileApplicationListener.DocumentConsumer consumer) {
    //通过getSearchLocations方法,获取配置文件的目录,默认classpath:/,classpath:/config/,file:./,file:./config/*/,file:./config/
    this.getSearchLocations().forEach((location) -> {
        boolean isDirectory = location.endsWith("/");
        //通过getSearchNames方法获取spring.config.name的配置值,默认为application
        Set names = isDirectory?this.getSearchNames():ConfigFileApplicationListener.NO_SEARCH_NAMES;
        names.forEach((name) -> {
            //根据文件路径location,文件名称name,激活的配置profile加载配置文件
            this.load(location, name, profile, filterFactory, consumer);
        });
    });
}
private void load(String location, String name, ConfigFileApplicationListener.Profile profile, ConfigFileApplicationListener.DocumentFilterFactory filterFactory, ConfigFileApplicationListener.DocumentConsumer consumer) {
    if(!StringUtils.hasText(name)) {
        Iterator var13 = this.propertySourceLoaders.iterator();
        PropertySourceLoader var14;
        do {
            if(!var13.hasNext()) {
                throw new IllegalStateException("File extension of config file location \'" + location + "\' is not known to any PropertySourceLoader. If the location is meant to reference a directory, it must end in \'/\'");
            }
            var14 = (PropertySourceLoader)var13.next();
        } while(!this.canLoadFileExtension(var14, location));

        this.load(var14, location, profile, filterFactory.getDocumentFilter(profile), consumer);
    } else {
        HashSet processed = new HashSet();
        //获取配置文件解析器,主要有PropertiesPropertySourceLoader(解析Properties和xml类型文件)和YamlPropertySourceLoader(解析yml和yaml文件)
        Iterator loader = this.propertySourceLoaders.iterator();
        while(loader.hasNext()) {
            PropertySourceLoader loader1 = (PropertySourceLoader)loader.next();
            //PropertiesPropertySourceLoader.getFileExtensions的值为Properties、xml,YamlPropertySourceLoader为yml、yaml
            String[] var9 = loader1.getFileExtensions();
            int var10 = var9.length;
            for(int var11 = 0; var11 < var10; ++var11) {
                String fileExtension = var9[var11];
                if(processed.add(fileExtension)) {
                    //根据文件路径location,文件名称name,文件后缀fileExtension,激活的配置profile加载配置文件
                    this.loadForFileExtension(loader1, location + name, "." + fileExtension, profile, filterFactory, consumer);
                }
            }
        }
    }
}
private void loadForFileExtension(PropertySourceLoader loader, String prefix, String fileExtension, ConfigFileApplicationListener.Profile profile, ConfigFileApplicationListener.DocumentFilterFactory filterFactory, ConfigFileApplicationListener.DocumentConsumer consumer) {
    ConfigFileApplicationListener.DocumentFilter defaultFilter = filterFactory.getDocumentFilter((ConfigFileApplicationListener.Profile)null);
    ConfigFileApplicationListener.DocumentFilter profileFilter = filterFactory.getDocumentFilter(profile);
    if(profile != null) {
        String profileSpecificFile = prefix + "-" + profile + fileExtension;
        this.load(loader, profileSpecificFile, profile, defaultFilter, consumer);
        this.load(loader, profileSpecificFile, profile, profileFilter, consumer);
        Iterator var10 = this.processedProfiles.iterator();
        
        while(var10.hasNext()) {
            //遍历激活的Profile,如dev或者test
            ConfigFileApplicationListener.Profile processedProfile = (ConfigFileApplicationListener.Profile)var10.next();
            if(processedProfile != null) {
                //拼接配置文件的路径和名称
                String previouslyLoaded = prefix + "-" + processedProfile + fileExtension;
                this.load(loader, previouslyLoaded, profile, profileFilter, consumer);
            }
        }
    }

    this.load(loader, prefix + fileExtension, profile, profileFilter, consumer);
}

 

private void load(PropertySourceLoader loader, String location, ConfigFileApplicationListener.Profile profile, ConfigFileApplicationListener.DocumentFilter filter, ConfigFileApplicationListener.DocumentConsumer consumer) {
    Resource[] resources = this.getResources(location);
    Resource[] var7 = resources;
    int var8 = resources.length;
    for(int var9 = 0; var9 < var8; ++var9) {
        Resource resource = var7[var9];
        try {
            StringBuilder ex;
            //判断文件是否存在
            if(resource != null && resource.exists()) {
                if(!StringUtils.hasText(StringUtils.getFilenameExtension(resource.getFilename()))) {
                } else {
                    String var17 = "applicationConfig: [" + this.getLocationName(location, resource) + "]";
                    //读取配置文件,解析成ConfigFileApplicationListener.Document类
                    List var18 = this.loadDocuments(loader, var17, resource);
                    if(CollectionUtils.isEmpty(var18)) {
                        if(this.logger.isTraceEnabled()) {
                            StringBuilder loaded = this.getDescription("Skipped unloaded config ", location, resource, profile);
                            this.logger.trace(loaded);
                        }
                    } else {
                        ArrayList var19 = new ArrayList();
                        Iterator description1 = var18.iterator();
                        while(description1.hasNext()) {
                            ConfigFileApplicationListener.Document document = (ConfigFileApplicationListener.Document)description1.next();
                            if(filter.match(document)) {
                                this.addActiveProfiles(document.getActiveProfiles());
                                this.addIncludedProfiles(document.getIncludeProfiles());
                                var19.add(document);
                            }
                        }
                        Collections.reverse(var19);
                        if(!var19.isEmpty()) {
                            var19.forEach((document) -> {
                                //循环调用ConfigFileApplicationListener.DocumentConsumer的accept方法,即addToLoaded返回的对象。
                                consumer.accept(profile, document);
                            });
                        }
                    }
                }
            } 
        } 
    }
}
private ConfigFileApplicationListener.DocumentConsumer addToLoaded(BiConsumer<MutablePropertySources, PropertySource<?>> addMethod, boolean checkForExisting) {
    return (profile, document) -> {
        if(checkForExisting) {
            Iterator merged = this.loaded.values().iterator();
            while(merged.hasNext()) {
                MutablePropertySources merged1 = (MutablePropertySources)merged.next();
                if(merged1.contains(document.getPropertySource().getName())) {
                    return;
                }
            }
        }
        //获取已经存在的profile对应的MutablePropertySources
        MutablePropertySources merged2 = (MutablePropertySources)this.loaded.computeIfAbsent(profile, (k) -> {
            return new MutablePropertySources();
        });
        //即调用MutablePropertySources的addFirst方法,将document中解析出的PropertySource,保存到profile对应的MutablePropertySources的最前面
        addMethod.accept(merged2, document.getPropertySource());
    };
}

配置文件解析完成之后,调用this.addLoadedPropertySources(), 遍历loaded中的所有MutablePropertySources 中的所有PropertySource,保存到environment的MutablePropertySources 中;

private void addLoadedPropertySources() {
    MutablePropertySources destination = this.environment.getPropertySources();
    ArrayList loaded = new ArrayList(this.loaded.values());
    Collections.reverse(loaded);
    String lastAdded = null;
    HashSet added = new HashSet();
    Iterator var5 = loaded.iterator();
    //遍历解析出来的保存在loaded中的MutablePropertySources 
    while(var5.hasNext()) {
        MutablePropertySources sources = (MutablePropertySources)var5.next();
        Iterator var7 = sources.iterator();
        //遍历每一个MutablePropertySources 中的
        while(var7.hasNext()) {
            PropertySource source = (PropertySource)var7.next();
            if(added.add(source.getName())) {
                //将配置source 保存到环境对应的MutablePropertySources的最后
                this.addLoadedPropertySource(destination, lastAdded, source);
                lastAdded = source.getName();
            }
        }
    }
}
private void addLoadedPropertySource(MutablePropertySources destination, String lastAdded, PropertySource<?> source) {
    if(lastAdded == null) {
        if(destination.contains("defaultProperties")) {
            destination.addBefore("defaultProperties", source);
        } else {
            destination.addLast(source);
        }
    } else {
        destination.addAfter(lastAdded, source);
    }
}

在上面的解析中只是解析了 带application前缀的配置文件,对于bootstrap前缀的配置文件是在BootstrapApplicationListener中解析的,BootstrapApplicationListener是ApplicationListener子类,监听ApplicationEnvironmentPreparedEvent,BootstrapApplicationListener的内部私有类AncestorInitializer实现了ApplicationContextInitializer

public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
    //获取上下文的的环境
    ConfigurableEnvironment environment = event.getEnvironment();
    if(((Boolean)environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class, Boolean.valueOf(true))).booleanValue()) {
        if(!environment.getPropertySources().contains("bootstrap")) {
            ConfigurableApplicationContext context = null;
            String configName = environment.resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}");
            //获取所有的ApplicationContextInitializer对象迭代遍历
            Iterator var5 = event.getSpringApplication().getInitializers().iterator();
            while(var5.hasNext()) {
                ApplicationContextInitializer initializer = (ApplicationContextInitializer)var5.next();
                if(initializer instanceof ParentContextApplicationContextInitializer) {
                    //获取已经存在的bootstrapContext上下文
                    context = this.findBootstrapContext((ParentContextApplicationContextInitializer)initializer, configName);
                }
            }
          
            if(context == null) {
               //开启一个新的上下文,bootstrapContext上下文在启动的上下文之前完成
                context = this.bootstrapServiceContext(environment, event.getSpringApplication(), configName);
                event.getSpringApplication().addListeners(new ApplicationListener[]{new BootstrapApplicationListener.CloseContextOnFailureApplicationListener(context)});
            }

            this.apply(context, event.getSpringApplication(), environment);
        }
    }
}
private ConfigurableApplicationContext bootstrapServiceContext(ConfigurableEnvironment environment, SpringApplication application, String configName) {
    //创建一个新的标准环境
    StandardEnvironment bootstrapEnvironment = new StandardEnvironment();
    //获取环境的配置文件
    MutablePropertySources bootstrapProperties = bootstrapEnvironment.getPropertySources();
    Iterator configLocation = bootstrapProperties.iterator();
    while(configLocation.hasNext()) {
        PropertySource configAdditionalLocation = (PropertySource)configLocation.next();
        bootstrapProperties.remove(configAdditionalLocation.getName());
    }
    String configLocation1 = environment.resolvePlaceholders("${spring.cloud.bootstrap.location:}");
    String configAdditionalLocation1 = environment.resolvePlaceholders("${spring.cloud.bootstrap.additional-location:}");
    HashMap bootstrapMap = new HashMap();
    //设置spring.config.name的配置为bootstrap
    bootstrapMap.put("spring.config.name", configName);
    bootstrapMap.put("spring.main.web-application-type", "none");
    if(StringUtils.hasText(configLocation1)) {
        bootstrapMap.put("spring.config.location", configLocation1);
    }
    if(StringUtils.hasText(configAdditionalLocation1)) {
        bootstrapMap.put("spring.config.additional-location", configAdditionalLocation1);
    }
    //将bootstrapMap配置文件的优先级放到最前面
    bootstrapProperties.addFirst(new MapPropertySource("bootstrap", bootstrapMap));
    Iterator builder = environment.getPropertySources().iterator();
    //遍历环境中已经存在的配置文件,重新保存到bootstrapProperties中,按顺序放到bootstrapMap后面
    while(builder.hasNext()) {
        PropertySource builderApplication = (PropertySource)builder.next();
        if(!(builderApplication instanceof StubPropertySource)) {
            bootstrapProperties.addLast(builderApplication);
        }
    }
    //设置新上下文的参数
    SpringApplicationBuilder builder1 = (new SpringApplicationBuilder(new Class[0])).profiles(environment.getActiveProfiles()).bannerMode(Mode.OFF).environment(bootstrapEnvironment).registerShutdownHook(false).logStartupInfo(false).web(WebApplicationType.NONE);
    SpringApplication builderApplication1 = builder1.application();
    if(builderApplication1.getMainApplicationClass() == null) {
        builder1.main(application.getMainApplicationClass());
    }
    if(environment.getPropertySources().contains("refreshArgs")) {
        builderApplication1.setListeners(this.filterListeners(builderApplication1.getListeners()));
    }
    builder1.sources(new Class[]{BootstrapImportSelectorConfiguration.class});
    //启动上下文,此时spring.config.name的配置为bootstrap,所以会读取bootstrap文件名称的相关配置;
    ConfigurableApplicationContext context = builder1.run(new String[0]);
    context.setId("bootstrap");
    //将BootstrapApplicationListener.AncestorInitializer添加到application上下文中,
    this.addAncestorInitializer(application, context);
    bootstrapProperties.remove("bootstrap");
    this.mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties);
    return context;
}

上面已经将带bootstrap前缀的配置文件加载到了bootstrapContext父上下文中,然后当前上下文applicationContext调用SpringApplication.applyInitializers方法

protected void applyInitializers(ConfigurableApplicationContext context) {
    Iterator var2 = this.getInitializers().iterator();
    while(var2.hasNext()) {
        ApplicationContextInitializer initializer = (ApplicationContextInitializer)var2.next();
        Class requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class);
        Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
        initializer.initialize(context);
    }
}

查看AncestorInitializer的initialize方法的,其创建了ParentContextApplicationContextInitializer对象,并调用了ParentContextApplicationContextInitializer.initialize方法,将bootstrapEnvironment对应的bootstrapContext上下文作为父上下文设置到当前的applicationContext中,从而将bootstrapEnvironment中的bootstrap相关的配置文件合并到applicationContext中,配置文件的优先级如下

public void initialize(ConfigurableApplicationContext context) {
    while(context.getParent() != null && context.getParent() != context) {
        context = (ConfigurableApplicationContext)context.getParent();
    }
    this.reorderSources(context.getEnvironment());
    (new ParentContextApplicationContextInitializer(this.parent)).initialize(context);
}
public void initialize(ConfigurableApplicationContext applicationContext) {
    if(applicationContext != this.parent) {
        applicationContext.setParent(this.parent);
        applicationContext.addApplicationListener(ParentContextApplicationContextInitializer.EventPublisher.INSTANCE);
    }

}

SpringBoot外部化配置

 

 

上一篇:SpringBoot源码(六): 创建ConfigurableEnvironment


下一篇:ubuntu18.04配置ROS2出现的问题:“ROS_DISTRO was set to ‘dashing‘ before ”