Spring Boot-源碼閱讀-如何加載環境變量(二)


說明

在Spring Boot-源碼閱讀-啟動主流程(一) 8-11處觸發了環境變量的加載

 

<1>

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
                                                       DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
        // <2>內部封裝的PropertySource集合 封裝各個環境變量 如默認會初始化系統環境變量 和jvm環境變量
        ConfigurableEnvironment environment = getOrCreateEnvironment();
        //將系統變量設置到environment
        configureEnvironment(environment, applicationArguments.getSourceArgs());      
        ConfigurationPropertySources.attach(environment);
        //<4>發送listener ApplicationEnviromentPreparedEvent 對應消費將觸發我們application.yml配置文件加載
        /**
         * # Run Listeners
         * org.springframework.boot.SpringApplicationRunListener=\
         * org.springframework.boot.context.event.EventPublishingRunListener 默認
         */
        listeners.environmentPrepared(bootstrapContext, environment);
        DefaultPropertiesPropertySource.moveToEnd(environment);
        Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
                "Environment prefix cannot be set via properties.");
        bindToSpringApplication(environment);
        if (!this.isCustomEnvironment) {
            environment = convertEnvironment(environment);
        }
        ConfigurationPropertySources.attach(environment);
        return environment;
    }

<2>

private ConfigurableEnvironment getOrCreateEnvironment() {
        if (this.environment != null) {
            return this.environment;
        }
        switch (this.webApplicationType) {
            case SERVLET:
                 //<3>默認返回此 父類會觸發初始化默認的PropertySource
                return new ApplicationServletEnvironment();
            case REACTIVE:
                return new ApplicationReactiveWebEnvironment();
            default:
                return new ApplicationEnvironment();
        }
    }

<3>

繼承鏈

 

 

 org.springframework.core.env.AbstractEnvironment

    public AbstractEnvironment() {
        this(new MutablePropertySources());
    }
    protected AbstractEnvironment(MutablePropertySources propertySources) {
        this.propertySources = propertySources;
//當我調用get 就是委托給Resolver 遍歷調用propertSource獲取對應的值。比如我們可以實現自定義key解析 get("${age}")內部委托給propertySroce就傳age
this.propertyResolver = createPropertyResolver(propertySources); //<3-1>被子類重寫 StandardServletEnvironment customizePropertySources(propertySources); }

<3-1>

org.springframework.web.context.support.StandardServletEnvironment#customizePropertySources

 public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams";
    public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams";
    protected void customizePropertySources(MutablePropertySources propertySources) {
        //新增2個servlet相關的propertySource 但是是空的
        propertySources.addLast(new PropertySource.StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
        propertySources.addLast(new PropertySource.StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
        if (jndiPresent && JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
            propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
        }
        //<3-2>調用父類StandardEnvironment 的自定義方法
        super.customizePropertySources(propertySources);
    }

<3-2>

org.springframework.core.env.StandardEnvironment#customizePropertySources

 public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
    public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
    @Override
    protected void customizePropertySources(MutablePropertySources propertySources) {
        //新增2各個System相關環境變量 並加載
        //內部調用System.getProperties() 獲得map jvm參數相關環境變量
        propertySources.addLast(
                new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
        //內部調用 (Map) System.getenv() 獲得map 主要獲取系統相關環境變量
        propertySources.addLast(
                new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
    }

<4>

  //EventPublishingRunListener構造函數
    public EventPublishingRunListener(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
        this.initialMulticaster = new SimpleApplicationEventMulticaster();
        //EventPublishingRunListener初始化的時候會初始化一個廣播器,注冊了我們ApplicationListener監聽器 初始化處可以查看<點擊跳轉>
        for (ApplicationListener<?> listener : application.getListeners()) {
            this.initialMulticaster.addApplicationListener(listener);
        }
    }
    public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
                                    ConfigurableEnvironment environment) {
        //<5>發送ApplicationEnvironmentPreparedEvent事件 將由監聽器org.springframework.boot.env.EnvironmentPostProcessorApplicationListener監聽處理
        this.initialMulticaster.multicastEvent(
                new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));
    }

<5>

我們主要關注org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor 這個就是如何加載application.yml

org.springframework.boot.env.EnvironmentPostProcessorApplicationListener#onApplicationEnvironmentPreparedEvent

    private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
        ConfigurableEnvironment environment = event.getEnvironment();
        SpringApplication application = event.getSpringApplication();
        //<6>這里也是獲取spring.factories的EnvironmentPostProcessor實現類 這里也是我們加載自定義環境變量的擴展點
        /**
         * # Environment Post Processors
         * org.springframework.boot.env.EnvironmentPostProcessor=\
         * org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
         * org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor,\
         * org.springframework.boot.env.RandomValuePropertySourceEnvironmentPostProcessor,\ 針對於隨機數可以自行百度 如@value(${random.int}),@value(${random.int[1,1024]}),
         * org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\ 針對json的配置如:-d {"age":12} @value(${age})
         * org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor,\
         * org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor
         */
        for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(),
                event.getBootstrapContext())) {
            postProcessor.postProcessEnvironment(environment, application);
        }
    }

<6>

org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor#postProcessEnvironment

 @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        postProcessEnvironment(environment, application.getResourceLoader(), application.getAdditionalProfiles());
    }
    void postProcessEnvironment(ConfigurableEnvironment environment, ResourceLoader resourceLoader,
                                Collection<String> additionalProfiles) {
        try {
            this.logger.trace("Post-processing environment to add config data");
            resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader();
            //委托給ConfigDataEnvironment 觸發加載<7>初始化 <10>加載
            getConfigDataEnvironment(environment, resourceLoader, additionalProfiles).processAndApply();
        }
        catch (UseLegacyConfigProcessingException ex) {
            this.logger.debug(LogMessage.format("Switching to legacy config file processing [%s]",
                    ex.getConfigurationProperty()));
            configureAdditionalProfiles(environment, additionalProfiles);
            postProcessUsingLegacyApplicationListener(environment, resourceLoader);
        }
    }

<7>

org.springframework.boot.context.config.ConfigDataEnvironment#ConfigDataEnvironment

    ConfigDataEnvironment(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext,
                          ConfigurableEnvironment environment, ResourceLoader resourceLoader, Collection<String> additionalProfiles,
                          ConfigDataEnvironmentUpdateListener environmentUpdateListener) {
        Binder binder = Binder.get(environment);
        UseLegacyConfigProcessingException.throwIfRequested(binder);
        this.logFactory = logFactory;
        this.logger = logFactory.getLog(getClass());
        this.notFoundAction = binder.bind(ON_NOT_FOUND_PROPERTY, ConfigDataNotFoundAction.class)
                .orElse(ConfigDataNotFoundAction.FAIL);
        this.bootstrapContext = bootstrapContext;
        this.environment = environment;
        this.resolvers = createConfigDataLocationResolvers(logFactory, bootstrapContext, binder, resourceLoader);
        this.additionalProfiles = additionalProfiles;
        this.environmentUpdateListener = (environmentUpdateListener != null) ? environmentUpdateListener
                : ConfigDataEnvironmentUpdateListener.NONE;
        this.loaders = new ConfigDataLoaders(logFactory, bootstrapContext, resourceLoader.getClassLoader());
        //<8>我的理解這個是解析器
        this.contributors = createContributors(binder);
    }

<8>

org.springframework.boot.context.config.ConfigDataEnvironment#createContributors

   private ConfigDataEnvironmentContributors createContributors(Binder binder) {
        this.logger.trace("Building config data environment contributors");
        MutablePropertySources propertySources = this.environment.getPropertySources();
        List<ConfigDataEnvironmentContributor> contributors = new ArrayList<>(propertySources.size() + 10);
        PropertySource<?> defaultPropertySource = null;
        for (PropertySource<?> propertySource : propertySources) {
            if (DefaultPropertiesPropertySource.hasMatchingName(propertySource)) {
                defaultPropertySource = propertySource;
            }
            else {
                this.logger.trace(LogMessage.format("Creating wrapped config data contributor for '%s'",
                        propertySource.getName()));
                contributors.add(ConfigDataEnvironmentContributor.ofExisting(propertySource));
            }
        }
        //<9>主要是這里創建負責解析指定目錄文件下配置文件的解析器
        contributors.addAll(getInitialImportContributors(binder));
        if (defaultPropertySource != null) {
            this.logger.trace("Creating wrapped config data contributor for default property source");
            contributors.add(ConfigDataEnvironmentContributor.ofExisting(defaultPropertySource));
        }
        return createContributors(contributors);
    }

<9>

org.springframework.boot.context.config.ConfigDataEnvironment#getInitialImportContributors

 static final ConfigDataLocation[] DEFAULT_SEARCH_LOCATIONS;
    static {
        List<ConfigDataLocation> locations = new ArrayList<>();
        locations.add(ConfigDataLocation.of("optional:classpath:/;optional:classpath:/config/"));
        locations.add(ConfigDataLocation.of("optional:file:./;optional:file:./config/;optional:file:./config/*/"));
        DEFAULT_SEARCH_LOCATIONS = locations.toArray(new ConfigDataLocation[0]);
    }

    private List<ConfigDataEnvironmentContributor> getInitialImportContributors(Binder binder) {
        List<ConfigDataEnvironmentContributor> initialContributors = new ArrayList<>();
        addInitialImportContributors(initialContributors, bindLocations(binder, IMPORT_PROPERTY, EMPTY_LOCATIONS));
        addInitialImportContributors(initialContributors,
                bindLocations(binder, ADDITIONAL_LOCATION_PROPERTY, EMPTY_LOCATIONS));
        //主要是這里新增了從哪些目錄下查找我們的properties文件 指定定義還未加載
        addInitialImportContributors(initialContributors,
                bindLocations(binder, LOCATION_PROPERTY, DEFAULT_SEARCH_LOCATIONS));
        return initialContributors;
    }

<10>

org.springframework.boot.context.config.ConfigDataEnvironment#processAndApply

    void processAndApply() {
        ConfigDataImporter importer = new ConfigDataImporter(this.logFactory, this.notFoundAction, this.resolvers,
                this.loaders);
        registerBootstrapBinder(this.contributors, null, DENY_INACTIVE_BINDING);
        //這里先加載我們的application.yml文件
        ConfigDataEnvironmentContributors contributors = processInitial(this.contributors, importer);
        ConfigDataActivationContext activationContext = createActivationContext(
                contributors.getBinder(null, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE));
        //根據application.yml配置的環境,加載對應的yml文件
        /**
         * 如
         * spring:
         *   profiles:
         *     active: dev,test
         */
        contributors = processWithoutProfiles(contributors, importer, activationContext);
        //解析出環境配置如 dev,test
        activationContext = withProfiles(contributors, activationContext);
        //進行對應環境的yml文件加載
        contributors = processWithProfiles(contributors, importer, activationContext);
        //轉成PropertySource add到Environment
        applyToEnvironment(contributors, activationContext, importer.getLoadedLocations(),
    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM