大多数Spring开发人员都知道BeanPostProcessor
和BeanFactoryPostProcessor
类。 前者启用对新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.level
和spring.aop.proxyTargetClass
属性(具有相关值)添加到环境中(如果尚不存在)。 如果这样做,它们将被添加到列表的底部。
使用@Conditional
,启动程序可以在自动配置类中提供默认bean,而使用EnvironmentPostProcessor
,它们也可以提供默认属性值。 在设计您自己的启动器时,将两者结合使用可以为在配置Spring Boot体验方面提供出色的约定大有帮助。