spring boot配置文件

https://www.cnblogs.com/junzisi/p/15480619.html
原文链接:https://blog.csdn.net/justuseit/article/details/115124277
https://blog.csdn.net/yaomingyang/article/details/109318562
https://blog.csdn.net/shijianjinghun/article/details/110492890
https://www.cnblogs.com/junzisi/p/15505983.html

第一阶段:获取事件监听器

起点-> new SpringApplication()

    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class))
    
-> SpringApplication.setListeners()
    this.listeners = new ArrayList<>(listeners)
    
        listeners = {ArrayList@1489}  size = 12
             0 = {BootstrapApplicationListener@1660} 
             1 = {LoggingSystemShutdownListener@1661} 
             2 = {EnvironmentPostProcessorApplicationListener@1662} 
             3 = {AnsiOutputApplicationListener@1663} 
             4 = {LoggingApplicationListener@1664} 
             5 = {BackgroundPreinitializer@1665} 
             6 = {DelegatingApplicationListener@1666} 
             7 = {RestartListener@1667} 
             8 = {ParentContextCloserApplicationListener@1668} 
             9 = {AppListener@1669} 
             10 = {ClearCachesApplicationListener@1670} 
             11 = {FileEncodingApplicationListener@1671} 

发布环境准备事件
-> SpirngApplication.prepareEnvironment()

    getOrCreateEnvironment()                                                       获取配置环境对象
    configureEnvironment()                                                         对AbstractEnvironment环境对象的propertySources属性进行赋值

        0 = {PropertySource$StubPropertySource@1933} "StubPropertySource {name='servletConfigInitParams'}"
        1 = {PropertySource$StubPropertySource@1934} "StubPropertySource {name='servletContextInitParams'}"
        2 = {PropertiesPropertySource@1935} "PropertiesPropertySource {name='systemProperties'}"
        3 = {SystemEnvironmentPropertySource@1936} "SystemEnvironmentPropertySource {name='systemEnvironment'}"
      一个servlet配置属性源,一个servlet上下文属性源,一个操作系统环境变量属性源,一个jvm环境变量属性源
      
    listeners.environmentPrepared()                                               通过SpringApplication的启动生命周期发布ApplicationEnvironmentPreparedEvent事件,
                                                                                  EnvironmentPostProcessorApplicationListener会监听onApplicationEnvironmentPreparedEvent事件,
                                                                                  回调到ConfigDataEnvironmentPostProcessor的postProcessEnvironment(),在其中从spring.factories中
                                                                                  获取ConfigDataLoader,ConfigDataLocationResolver 加载解析核心组件,并构造成ConfigDataEnvironment对象
                                

-> listeners.environmentPrepared(bootstrapContext, environment)                     发送环境已准备事件ApplicationEnvironmentPreparedEvent:
                                                                                    对application.properties/application.yaml的配置文件解析   

  
  
第二阶段:在监听器EnvironmentPostProcessorApplicationListener的onApplicationEvent处理环境准备事件
  
-> EnvironmentPostProcessorApplicationListener.onApplicationEvent()
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationEnvironmentPreparedEvent environmentPreparedEvent) {
            onApplicationEnvironmentPreparedEvent(environmentPreparedEvent);
        }
        if (event instanceof ApplicationPreparedEvent) {
            onApplicationPreparedEvent();
        }
        if (event instanceof ApplicationFailedEvent) {
            onApplicationFailedEvent();
        }
    }  
  
  
  进入EnvironmentPostProcessorApplicationListener的onApplicationEvent方法,前面发送的就是ApplicationEnvironmentPreparedEvent事件
-> EnvironmentPostProcessorApplicationListener.onApplicationEnvironmentPreparedEvent()

    private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
        ConfigurableEnvironment environment = event.getEnvironment();
        SpringApplication application = event.getSpringApplication();
        for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(),
                event.getBootstrapContext())) {
            postProcessor.postProcessEnvironment(environment, application);
        }
    }

  进入onApplicationEnvironmentPreparedEvent,这里面会获取环境后置处理器,也可以叫环境增强器,EnvironmentPostProcessor然后遍历执行
  getEnvironmentPostProcessors(application.getResourceLoader(), event.getBootstrapContext())
 
        result = {ArrayList@2276}  size = 12
         0 = {RandomValuePropertySourceEnvironmentPostProcessor@2278} 
         1 = {CachedRandomPropertySourceEnvironmentPostProcessor@2279} 
         2 = {SystemEnvironmentPropertySourceEnvironmentPostProcessor@2280}                    把环境上下文中名称为'systemEnvironment'的PropertySource替换掉
                                                                                                

         3 = {SpringApplicationJsonEnvironmentPostProcessor@2281}                              将系统属性spring.application.json或SPRING_APPLICATION_JSON解析成JSON,
                                                                                               并存储到Environment
         4 = {CloudFoundryVcapEnvironmentPostProcessor@2282} 
         5 = {HostInfoEnvironmentPostProcessor@2283} 
         6 = {ConfigDataEnvironmentPostProcessor@2284}                                         重点:
         7 = {BootstrapConfigFileApplicationListener@2285}                                     重点:
         8 = {NacosConfigDataMissingEnvironmentPostProcessor@2286} 
         9 = {DebugAgentEnvironmentPostProcessor@2287} 
         10 = {IntegrationPropertiesEnvironmentPostProcessor@2288} 
         11 = {DecryptEnvironmentPostProcessor@2289} 
    
    
    获取环境后置处理器,遍历执行,其实我们真正关注的,是对配置文件进行解析的类是ConfigDataEnvironmentPostProcessor。
    以前我们的配置文件的优先级是由ConfigFileApplicationListener处理的;从2.4开始,改成了ConfigDataEnvironmentPostProcessor。


------------------------------------------------------------------------------------进入ConfigDataEnvironmentPostProcessor 
-> ConfigDataEnvironmentPostProcessor.postProcessEnvironment()
    getConfigDataEnvironment(environment, resourceLoader, additionalProfiles).processAndApply()

-> new ConfigDataEnvironment()
    new ConfigDataEnvironment(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext,
       ConfigurableEnvironment environment, ResourceLoader resourceLoader, Collection<String> additionalProfiles) {
       Binder binder = Binder.get(environment); //1. 绑定当前Environment对象
       UseLegacyConfigProcessingException.throwIfRequested(binder);
       this.logFactory = logFactory;
       this.logger = logFactory.getLog(getClass());
       
       //2. 从属性spring.config.on-not-found中获取文件找不到的执行逻辑
       this.notFoundAction = binder.bind(ON_NOT_FOUND_PROPERTY, ConfigDataNotFoundAction.class)
             .orElse(ConfigDataNotFoundAction.FAIL);
       this.bootstrapContext = bootstrapContext;
       this.environment = environment;
       //3. 从spring.factories中获取ConfigDataLocationResolver实现。(可以自己实现,扩展点之一)
       //4. 同时这里面会传入boostrapper/resourceLoader/Binder等参数用于构造参数反射
       this.resolvers = createConfigDataLocationResolvers(logFactory, bootstrapContext, binder, resourceLoader);
       this.additionalProfiles = additionalProfiles;
       
       //5. 从spring.factories中获取所有的ConfigDataLoader并用反射进行实例化
       this.loaders = new ConfigDataLoaders(logFactory, bootstrapContext);
       
       //6. 创建ConfigDataEnvironmentContributors对象,里面会根据spring.config.import / location等默认定位参数初始化Contributor
       this.contributors = createContributors(binder);
    }    
-> new ConfigDataLocationResolvers()
-> new ConfigDataLoaders()
-> new ConfigDataEnvironmentContributors()
    
-> ConfigDataEnvironment.processAndApply()                                                                             开始加载配置文件逻辑
                                   
    
    void processAndApply() {
       //1. 封装ConfigDataImporter对象,里面有解析ConfigDataLocation -> ConfigDataResource 和load ConfigDataResource -> ConfigData之类的操作
       ConfigDataImporter importer = new ConfigDataImporter(this.logFactory, this.notFoundAction, this.resolvers,
             this.loaders);
       this.bootstrapContext.register(Binder.class, InstanceSupplier
             .from(() -> this.contributors.getBinder(null, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE)));
             
       //1. 加载和解析ConfigDataLocation -> ConfigDataResource -> ConfigData ,此时还没有导入到Environment中,
       执行完毕之后应该都是BOUND_IMPORT,且此时绑定了spring.config / spring.profiles相关的配置属性信息
       ConfigDataEnvironmentContributors contributors = processInitial(this.contributors, importer);
       
       //2. 获取包含Root Contributor中 所有ConfigurationPropertySource的Binder
       Binder initialBinder = contributors.getBinder(null, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE);
       
       //3. 重新注册Binder到Bootstrapper中
       this.bootstrapContext.register(Binder.class, InstanceSupplier.of(initialBinder));
       
       ConfigDataActivationContext activationContext = createActivationContext(initialBinder); //构建激活的上下文对象,此时对元计算平台进行设置
       //4. 带云计算平台参数上下文进行二次迭代
       contributors = processWithoutProfiles(contributors, importer, activationContext);
       
       //5. 构建profile
       activationContext = withProfiles(contributors, activationContext);
       //6. 带profile参数进行第三次迭代
       contributors = processWithProfiles(contributors, importer, activationContext);
       //7. 应用到Environment对象中
       applyToEnvironment(contributors, activationContext);
    }

-> ConfigDataEnvironment.processInitial()


    三大步
    1、通过ConfigDataLocationResolver将相关spring.config.import,spring.config.addtional-location,spring.config.location等资源定位路径下
    的spring.config.name-{profile}.fileExtension资源解析成ConfigDataResource
    
    2、通过ConfigDataLoader将ConfigDataLocationResolver解析好的资源进行加载,将ConfigDataResource -> ConfigData , 其中ConfigData是一组ProeprtySource
    
    3、将加载好的ConfigData添加到Environment中

    private ConfigDataEnvironmentContributors processInitial(ConfigDataEnvironmentContributors contributors,
            ConfigDataImporter importer) {
        this.logger.trace("Processing initial config data environment contributors without activation context");
        contributors = contributors.withProcessedImports(importer, null);
        registerBootstrapBinder(contributors, null, DENY_INACTIVE_BINDING);
        return contributors;
    }

-> ConfigDataEnvironmentContributors.withProcessedImports()    

    处理 Kind为INITIAL_IMPORT类型的Contributros ,这里面也是主要的解析配置的地方
        调用ConfigDataEnvironmentContributors.withProcessedImports()
        
    ConfigDataEnvironmentContributors withProcessedImports(ConfigDataImporter importer,
            ConfigDataActivationContext activationContext) {
        //1. 获取Import阶段,分导入前导入后
        ImportPhase importPhase = ImportPhase.get(activationContext);
        this.logger.trace(LogMessage.format("Processing imports for phase %s. %s", importPhase,
                (activationContext != null) ? activationContext : "no activation context"));
        ConfigDataEnvironmentContributors result = this;
        int processed = 0;
        while (true) {
            //1阶段. 初始化为null
            //2阶段. 设置好ActivationContext(相关云计算平台参数进行第二轮的迭代),进行相关云平台过滤
            //3阶段. 进行profile文件的解析和加载
            ConfigDataEnvironmentContributor contributor = getNextToProcess(result, activationContext, importPhase);
            if (contributor == null) {
                this.logger.trace(LogMessage.format("Processed imports for of %d contributors", processed));
                return result;
            }
            if (contributor.getKind() == Kind.UNBOUND_IMPORT) {
                //从UNBOUND_IMPORT Contributor中获取配置属性源
                Iterable<ConfigurationPropertySource> sources = Collections
                        .singleton(contributor.getConfigurationPropertySource());
                // 进行占位符解析
                PlaceholdersResolver placeholdersResolver = new ConfigDataEnvironmentContributorPlaceholdersResolver(
                        result, activationContext, true);
                Binder binder = new Binder(sources, placeholdersResolver, null, null, null);
                ConfigDataEnvironmentContributor bound = contributor.withBoundProperties(binder);
                // 绑定ConfigDataProperties 并进行替换
                result = new ConfigDataEnvironmentContributors(this.logger, this.bootstrapContext,
                        result.getRoot().withReplacement(contributor, bound));
                continue;
            }
            //2.封装Resolver,Loader等相关操作上下文对象
            ConfigDataLocationResolverContext locationResolverContext = new ContributorConfigDataLocationResolverContext(
                    result, contributor, activationContext);
            ConfigDataLoaderContext loaderContext = new ContributorDataLoaderContext(this);
            //3. 从ConfigDataLocationContributor(ConfigDataProperties)中获取ConfigDataLocation(资源路径对象)
            List<ConfigDataLocation> imports = contributor.getImports();
            this.logger.trace(LogMessage.format("Processing imports %s", imports));
            //4. 解析到Map<ConfigDataResource, ConfigData>
            Map<ConfigDataResource, ConfigData> imported = importer.resolveAndLoad(activationContext, 
                    locationResolverContext, loaderContext, imports);                                         解析configDataLocation为ConfigDataResource,
                                                                                                              随后ConfingDataLoader#load为ConfigData,并返回Map映射关系
                    
                    
            this.logger.trace(LogMessage.of(() -> imported.isEmpty() ? "Nothing imported" : "Imported "
                    + imported.size() + " resource " + ((imported.size() != 1) ? "s" : "") + imported.keySet()));
            ConfigDataEnvironmentContributor contributorAndChildren = contributor.withChildren(importPhase,
                    asContributors(imported));
            result = new ConfigDataEnvironmentContributors(this.logger, this.bootstrapContext, //返回设置好Child Contributor的结果集,然后继续下一次迭代
                    result.getRoot().withReplacement(contributor, contributorAndChildren));
            processed++;
        }
    }

-> ConfigDataImporter.resolveAndLoad()
    Map<ConfigDataResource, ConfigData> resolveAndLoad(ConfigDataActivationContext activationContext,
          ConfigDataLocationResolverContext locationResolverContext, ConfigDataLoaderContext loaderContext,
          List<ConfigDataLocation> locations) {
      try {
          //1. 初始化import阶段profile为空 , 这个第三阶段会派上用场
          Profiles profiles = (activationContext != null) ? activationContext.getProfiles() : null;
          //2. 使用ConfigDateLocationResolver进行加载和解析
          // ConfigDataResolutionResult 包含了ConfigDataLocation 和ConfigDataResource(解析结果)
          List<ConfigDataResolutionResult> resolved = resolve(locationResolverContext, profiles, locations);
          //3. 使用ConfigDataLoader将ConfigDataResource -> ConfigData -> (PropertySource)
          return load(loaderContext, resolved);
      }
      catch (IOException ex) {
          throw new IllegalStateException("IO error on loading imports from " + locations, ex);
      }
    }
-> ConfigDataImporter.resolve()
    
    private List<ConfigDataResolutionResult> resolve(ConfigDataLocationResolver<?> resolver,
              ConfigDataLocationResolverContext context, ConfigDataLocation location, Profiles profiles) {
         //进行解析
          List<ConfigDataResolutionResult> resolved = resolve(location, () -> resolver.resolve(context, location));
          if (profiles == null) {
              return resolved;
          }
          //下面是第三阶段用来进行profile环境加载
          List<ConfigDataResolutionResult> profileSpecific = resolve(location,
                  () -> resolver.resolveProfileSpecific(context, location, profiles));
          return merge(resolved, profileSpecific);
    }

-> ConfigDataImport.load()                                                     使用刚才加载到的ConfigDataResource列表进行ConfigDataLoader#load加载
    //我们可以通过在META-INF/spring.factories中配置我们自己实现的ConfigDataLoader进行扩展加载其他格式的外部化环境,
    // 比如最后我会演示扩展实现一个加载json文件的Loader
      private Map<ConfigDataResource, ConfigData> load(ConfigDataLoaderContext loaderContext,
              List<ConfigDataResolutionResult> candidates) throws IOException {
          Map<ConfigDataResource, ConfigData> result = new LinkedHashMap<>();
          //1. 从后向前迭代ConfigDataResolutionResult(包含ConfigDataLocation,ConfigDataResource)
        //2. 这里有个细节,为什么是从后往前遍历?因为之前解析profile的时候是从优先级低 -> 高
          for (int i = candidates.size() - 1; i >= 0; i--) {
              ConfigDataResolutionResult candidate = candidates.get(i);
              ConfigDataLocation location = candidate.getLocation();
              ConfigDataResource resource = candidate.getResource();
              if (this.loaded.add(resource)) {  
                  try {
                      //2. ConfigDataLoader加载将ConfigDataResource -> ConfigData (PropetySource)又是一个扩展点

                      ConfigData loaded = this.loaders.load(loaderContext, resource);                       loaders = {ArrayList@4421}  size = 3
                                                                                                                 0 = {ConfigTreeConfigDataLoader@4424} 
                                                                                                                 1 = {StandardConfigDataLoader@4425} 
                                                                                                                 2 = {NacosConfigDataLoader@4426} 
                      
                      if (loaded != null) {
                          result.put(resource, loaded);
                      }
                  }
                  catch (ConfigDataNotFoundException ex) {
                      handle(ex, location);
                  }
              }
          }
          return Collections.unmodifiableMap(result);
      }

->       
      
      

    至此,核心的三步我们就完成了两步,解析和加载,随后就是一些重复逻辑,加载另外两阶段的配置,这边挑一些细节来展示,
    我们回到ConfigDataEnvironment#processAndApply(),刚刚执行完processInitia()方法逻辑,解析和加载了第一阶段,
    随后进行云计算厂商的配置整合,核心在createActivationContext()
                            
执行完processInitial方法回到processAndApply方法,执行createActivationContext()
-> ConfigDataEnvironment.createActivationContext()
-> new ConfigDataActivationContext()

    new ConfigDataActivationContext(Environment environment, Binder binder) {
        this.cloudPlatform = deduceCloudPlatform(environment, binder);
        this.profiles = null;
    }
-> ConfigDataActivationContext.deduceCloudPlatform()                            
    private CloudPlatform deduceCloudPlatform(Environment environment, Binder binder) {
          for (CloudPlatform candidate : CloudPlatform.values()) {
              //尝试从Environment上下文中获取spring.main.cloud-platform,若有指定对应的云计算厂商则直接返回对应的CloudPlatform
              if (candidate.isEnforced(binder)) {
                  return candidate;
              }
          }
          //从环境变量中寻找是否有对应云平台的环境变量参数,比如k8s(svc相关环境参数): KUBERNETES_SERVICE_HOST/KUBERNETES_SERVICE_PORT
          return CloudPlatform.getActive(environment);
      }

-> ConfigDataEnvironment.processWithoutProfiles()
-> ConfigDataEnvironment.withProfiles()
    
    private ConfigDataActivationContext withProfiles(ConfigDataEnvironmentContributors contributors,                           获取Environment中的profile属性
              ConfigDataActivationContext activationContext) {
          this.logger.trace("Deducing profiles from current config data environment contributors");
          Binder binder = contributors.getBinder(activationContext, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE);
          try {
              //优先设置构造SpringApplication的addtionalProfile
              Set<String> additionalProfiles = new LinkedHashSet<>(this.additionalProfiles);
          //设置include profile
              additionalProfiles.addAll(getIncludedProfiles(contributors, activationContext));
          //设置active profile 、 default profile
              Profiles profiles = new Profiles(this.environment, binder, additionalProfiles);
              return activationContext.withProfiles(profiles);
          }
          catch (BindException ex) {
              if (ex.getCause() instanceof InactiveConfigDataAccessException) {
                  throw (InactiveConfigDataAccessException) ex.getCause();
              }
              throw ex;
    }

-> ConfigDataEnvironment.processWithProfiles()                                                                                带profile参数进行第三次迭代
  
   contributors = processWithProfiles(contributors, importer, activationContext);

-> ConfigDataEnvironment.applyToEnvironment(contributors, activationContext)                                                   应用到Environment对象中

------------------------------------------------------------------------------------进入BootstrapConfigFileApplicationListener

既是事件监听器,也是EnvironmentPostProcessor后置处理器

           
 
                                 

       
           
           
           
           
           
           
           
 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值