大多数Spring开发人员都知道BeanPostProcessorBeanFactoryPostProcessor类。 前者启用对新bean实例的更改,然后再使用它们,而后者则允许您修改bean定义-创建bean的元数据。 常见用例包括:

  • 通过ConfigurationClassPostProcessor@Configuration类进行引导处理
  • 通过PropertyPlaceholderConfigurer解决${…}占位符
  • 自动装配带注释的字段,setter方法和任意配置方法AutowiredAnnotationBeanPostProcessor
  • 依此类推...等等

开箱即用的和自定义的后处理器足以满足有关Spring Framework的大多数要求。

然后是Spring Boot,它的约定优于配置方法。 它的主要功能之一就是能够从不同的源读取配置,例如从默认application.properties ,default application.yml ,另一个配置文件和/或在命令行中传递的System属性。 幕后发生的事情是它们全部合并到一个Environment实例中。 然后可以通过传递键将该对象注入任何bean中并查询任何值。

作为入门设计师,如何定义默认配置值? 显然,不能通过Spring @Bean方法来设置它。 让我们以Spring Cloud Sleuth启动器为例进行分析:

Spring Cloud Sleuth从Dapper,Zipkin和HTrace大量借鉴了Spring Cloud的分布式跟踪解决方案。 对于大多数用户而言,Sleuth应该是不可见的,并且您与外部系统的所有交互都应自动进行检测。 您可以简单地在日志中捕获数据,也可以将其发送到远程收集器服务。

关于配置,启动程序更改默认日志格式以显示其他信息(具体来说,跨度和跟踪ID,但这与发布无关)。 让我们进一步探讨。

对于自动配置类,魔术始于Spring Cloud Sleuth启动程序JAR的META-INF/spring.factories文件:

# Environment Post Processor
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.cloud.sleuth.autoconfig.TraceEnvironmentPostProcessor

接口的定义如下所示:

publicinterfaceEnvironmentPostProcessor{
  voidpostProcessEnvironment(ConfigurableEnvironmentenvironment,SpringApplicationapplication);
}

像这样的实现:

publicclassTraceEnvironmentPostProcessorimplementsEnvironmentPostProcessor{

  privatestaticfinalStringPROPERTY_SOURCE_NAME="defaultProperties";

  @Override
  publicvoidpostProcessEnvironment(ConfigurableEnvironmentenvironment,SpringApplicationapplication){
    Map<String,Object>map=newHashMap<String,Object>();
    map.put("logging.pattern.level",
      "%clr(%5p) %clr([${spring.application.name:},%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{X-Span-Export:-}]){yellow}");
    map.put("spring.aop.proxyTargetClass","true");
    addOrReplace(environment.getPropertySources(),map);
  }

  privatevoidaddOrReplace(MutablePropertySourcespropertySources,Map<String,Object>map){
    MapPropertySourcetarget=null;
    if(propertySources.contains(PROPERTY_SOURCE_NAME)){
      PropertySource<?>source=propertySources.get(PROPERTY_SOURCE_NAME);
      if(sourceinstanceofMapPropertySource){
        target=(MapPropertySource)source;
        for(Stringkey:map.keySet()){
          if(!target.containsProperty(key)){
            target.getSource().put(key,map.get(key));
          }
        }
      }
    }
    if(target==null){
      target=newMapPropertySource(PROPERTY_SOURCE_NAME,map);
    }
    if(!propertySources.contains(PROPERTY_SOURCE_NAME)){
      propertySources.addLast(target);
    }
  }
}

可以看到,该实现将把logging.pattern.levelspring.aop.proxyTargetClass属性(具有相关值)添加到环境中(如果尚不存在)。 如果这样做,它们将被添加到列表的底部。

使用@Conditional ,启动程序可以在自动配置类中提供默认bean,而使用EnvironmentPostProcessor ,它们也可以提供默认属性值。 在设计您自己的启动器时,将两者结合使用可以为在配置Spring Boot体验方面提供出色的约定大有帮助。