spring boot application

spring boot默認已經配置了很多環境變量,例如,tomcat的默認端口是8080,項目的contextpath是“/”等等,spring boot允許你自定義一個application.properties文件,然後放在以下的地方,來重寫spring boot的環境變量

spring對配置application.properties的加載過程:

  1. 服務啓動調用:SpringApplication.run
  2. 創建默認的環境參數:ConfigurableEnvironment
  3. 觸發事件:ApplicationEnvironmentPreparedEvent
  4. 完成加載

整個過程主要使用spring boot 內置的ConfigFileApplicationListener監聽器監聽ApplicationEnvironmentPreparedEvent事件完成對application.properties加載以及設置。


下面我們來跟蹤源碼,看下spring boot是怎樣完成對application.properties文件的加載

  • SpringApplication 入口 run:
public ConfigurableApplicationContext run(String... args) {
        //無關的代碼暫略
        .......
        ConfigurableApplicationContext context = null;
        FailureAnalyzers analyzers = null;
        configureHeadlessProperty();
        //獲取執行監聽器實例
        SpringApplicationRunListeners listeners = getRunListeners(args);
        ........
            //創建全局系統參數實例
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                    args);
            //創建    ConfigurableEnvironment 並觸發ApplicationEnvironmentPreparedEvent事件    
            //加載配置的核心地方,spring啓動首要做的事情
            ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);
      .........
    }

prepareEnvironment方法

private ConfigurableEnvironment prepareEnvironment(
            SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments) {
        // Create and configure the environment
        //創建一個配置環境信息,當是web環境時創建StandardServletEnvironment實例,非web環境時創建StandardEnvironment實例
        ConfigurableEnvironment environment = getOrCreateEnvironment();
        configureEnvironment(environment, applicationArguments.getSourceArgs());
        //核心事件觸發方法,此方法執行後會執行所有監聽ApplicationEnvironmentPreparedEvent事件的監聽器,這裏我們是跟蹤application.properties文件的加載,就查看ConfigFileApplicationListener監聽器都做了什麼工作
        listeners.environmentPrepared(environment);
        if (!this.webEnvironment) {
            environment = new EnvironmentConverter(getClassLoader())
                    .convertToStandardEnvironmentIfNecessary(environment);
        }
        return environment;
    }
  • ConfigFileApplicationListener:
public void onApplicationEvent(ApplicationEvent event) {
        //從此處可以看到當事件爲ApplicationEnvironmentPreparedEvent時,執行onApplicationEnvironmentPreparedEvent方法
        if (event instanceof ApplicationEnvironmentPreparedEvent) {
            onApplicationEnvironmentPreparedEvent(
                    (ApplicationEnvironmentPreparedEvent) event);
        }
        if (event instanceof ApplicationPreparedEvent) {
            onApplicationPreparedEvent(event);
        }
    }

onApplicationEnvironmentPreparedEvent

private void onApplicationEnvironmentPreparedEvent(
            ApplicationEnvironmentPreparedEvent event) {
        //此處通過SpringFactoriesLoader加載EnvironmentPostProcessor所有擴展   
        List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
        //因爲此監聽器同樣是EnvironmentPostProcessor的擴展實例,所以在此處將自己加入集合
        postProcessors.add(this);
        AnnotationAwareOrderComparator.sort(postProcessors);
        //遍歷所有的EnvironmentPostProcessor擴展調用postProcessEnvironment
        //當然我們跟蹤是application.properties所以主要查看當前實例的postProcessEnvironment方法
        for (EnvironmentPostProcessor postProcessor : postProcessors) {
            postProcessor.postProcessEnvironment(event.getEnvironment(),
                    event.getSpringApplication());
        }
    }

postProcessEnvironment

@Override
    public void postProcessEnvironment(ConfigurableEnvironment environment,
            SpringApplication application) {
        //此處添加配置信息到environment實例中,此方法完成後就將application.properties加載到環境信息中
        addPropertySources(environment, application.getResourceLoader());
        configureIgnoreBeanInfo(environment);
        bindToSpringApplication(environment, application);
    }

addPropertySources

protected void addPropertySources(ConfigurableEnvironment environment,
            ResourceLoader resourceLoader) {
        //這裏先添加一個Random名稱的資源到環境信息中  
        RandomValuePropertySource.addToEnvironment(environment);
        //通過Loader加載application.properties並將信息存入環境信息中
        new Loader(environment, resourceLoader).load();
    }

load

public void load() {
//創建一個資源加載器,spring boot默認支持PropertiesPropertySourceLoader,YamlPropertySourceLoader兩種配置文件的加載
            this.propertiesLoader = new PropertySourcesLoader();
            this.activatedProfiles = false;
            //加載配置profile信息,默認爲default
            ..........此處省略
            while (!this.profiles.isEmpty()) {
                Profile profile = this.profiles.poll();
                //遍歷所有查詢路徑,默認路徑有:classpath:/,classpath:/config/,file:./,file:./config/
                for (String location : getSearchLocations()) {
                //這裏不僅僅是加載application.properties,當搜索路徑不是以/結束,默認認爲是文件名已存在的路徑
                    if (!location.endsWith("/")) {
                        // location is a filename already, so don't search for more
                        // filenames
                        load(location, null, profile);
                    }
                    else {
                         //遍歷要加載的文件名集合,默認爲application
                        for (String name : getSearchNames()) {
                            load(location, name, profile);
                        }
                    }
                }
                this.processedProfiles.add(profile);
            }

            //將加載完成的配置信息全部保存到環境信息中共享
            addConfigurationProperties(this.propertiesLoader.getPropertySources());
        }

load

private void load(String location, String name, Profile profile) {
            //此處根據profile組裝加載的文件名稱以及資源所放置的組信息
            String group = "profile=" + (profile == null ? "" : profile);
            if (!StringUtils.hasText(name)) {
                // Try to load directly from the location
                loadIntoGroup(group, location, profile);
            }
            else {
             
                    // Also try the profile-specific section (if any) of the normal file
                    loadIntoGroup(group, location + name + "." + ext, profile);
                }
            }
        }

loadIntoGroup

private PropertySource<?> doLoadIntoGroup(String identifier, String location,
                Profile profile) throws IOException {
            Resource resource = this.resourceLoader.getResource(location);
            PropertySource<?> propertySource = null;
            if (resource != null && resource.exists()) {
                String name = "applicationConfig: [" + location + "]";
                String group = "applicationConfig: [" + identifier + "]";
                //資源加載核心方法,此處有兩個實現,當後綴爲,xml或者properties調用PropertiesPropertySourceLoader
                //當後綴爲yml或者yaml時,調用YamlPropertySourceLoader
                
                propertySource = this.propertiesLoader.load(resource, 
            }
        
            return propertySource;
        }
  • PropertiesPropertySourceLoader:
@Override
    public PropertySource<?> load(String name, Resource resource, String profile)
            throws IOException {
        if (profile == null) {
            //此處調用PropertiesLoaderUtils工具類加載本地文件
            Properties properties = PropertiesLoaderUtils.loadProperties(resource);
            if (!properties.isEmpty()) {
                return new PropertiesPropertySource(name, properties);
            }
        }
        return null;
    }

到此application.properties就真正的加載並共享到環境信息中,供系統其它地方調用



 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章