在之前的文章中已经大致解释META-INF/spring.factories的作用以及加载流程,本章项目需要实现一些示例一下示例。
配置类加载
首先是配置加载实现的加载实现:
org.springframework.cloud.bootstrap.BootstrapConfiguration:表示org.springframework.cloud.bootstrap.config.PropertySourceLocator 的实现类实现的配置加载过程。
org.springframework.boot.env.PropertySourceLoade :表示org.springframework.boot.env.PropertySourceLoader的实现类实现的解析application配置文件。
org.springframework.boot.env.EnvironmentPostProcessor :表示org.springframework.boot.env.EnvironmentPostProcessor的实现类实现的自定义配置加载过程。
org.springframework.boot.env.EnvironmentPostProcessor
类实现:
public class MyEnvironmentPostProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
Properties properties = new Properties();
properties.put("EnvironmentPostProcessor.key1", "EnvironmentPostProcessor-value1");
properties.put("EnvironmentPostProcessor.key2", "EnvironmentPostProcessor-value2");
environment.getPropertySources().addLast(new PropertiesPropertySource("myProperties", properties));
System.out.println("MyEnvironmentPostProcessor ------ EnvironmentPostProcessor");
}
}
spring.factories配置:
org.springframework.boot.env.EnvironmentPostProcessor=\ zhong.test.springbootdemo.usultestdemo.configration.propertiespropertysource.myconfiguretation.MyEnvironmentPostProcessor
测试:
@SpringBootApplication(scanBasePackages = "zhong.test.springbootdemo.usultestdemo")
public class UsulTestStartApplication implements CommandLineRunner {
@Value("${EnvironmentPostProcessor.key1}")
String value1;
@Override
public void run(String... args) throws Exception {
System.out.println("key = " + value);
System.out.println("EnvironmentPostProcessor.key1 = " + value1);
}
public static void main(String[] args) {
try {
SpringApplication.run(UsulTestStartApplication.class, args);
} catch (Exception e) {
e.printStackTrace();
}
}
}
输出 EnvironmentPostProcessor.key1 = EnvironmentPostProcessor-value1
在application。properties中加入 EnvironmentPostProcessor.key1=1111111。结果发现打印的是 EnvironmentPostProcessor.key1 = 1111111。现象说明 配置中优先级 application.properties > 自定义EnvironmentPostProcessor 配置。
org.springframework.boot.env.PropertySourceLoade
PropertySourceLoade是对application配置文件的解析。所以文件的命名必须是applcation.XXX的方式。因为springboot中已经默认解析了applcation.properties,applcation.xml、applcation.yml。所以我们的自定义配置如果使用这三种方式,将使用自动使用的是springboot的解析方式,而不是我们自己的定义的解析方式。
spring.factories配置:
org.springframework.boot.env.EnvironmentPostProcessor=\ zhong.test.springbootdemo.usultestdemo.configration.propertiespropertysource.myconfiguretation.MyEnvironmentPostProcessor
类实现:
public class MyPropertySourceLoader implements PropertySourceLoader {
/**
* 解析的自定义文件的类型(文件必须以 application开始命名).因为spingboot默认会解析 application-*.properties和 application-*.xml 所以自定义不使用这两种文件类型
* @return
*/
@Override
public String[] getFileExtensions() {
return new String[]{"json"};
}
@Override
public List<PropertySource<?>> load(String name, Resource resource) throws IOException {
Map<String, ?> properties = mapPropertySource(resource);
if (properties.isEmpty()) {
return Collections.emptyList();
}
return Collections
.singletonList(new OriginTrackedMapPropertySource(name, properties));
}
/**
*
*解析json文件
*/
private Map<String, Object> mapPropertySource(Resource resource) throws IOException {
if (resource == null) {
return null;
}
Map<String, Object> result = new HashMap<String, Object>();
JsonParser parser = JsonParserFactory.getJsonParser();
Map<String, Object> map = parser.parseMap(readFile(resource));
nestMap("", result, map);
return result;
}
private String readFile(Resource resource) throws IOException {
InputStream inputStream = resource.getInputStream();
List<Byte> byteList = new LinkedList<Byte>();
byte[] readByte = new byte[1024];
int length;
while ((length = inputStream.read(readByte)) > 0) {
for (int i = 0; i < length; i++) {
byteList.add(readByte[i]);
}
}
byte[] allBytes = new byte[byteList.size()];
int index = 0;
for (Byte soloByte : byteList) {
allBytes[index] = soloByte;
index += 1;
}
return new String(allBytes, "UTF-8");
}
@SuppressWarnings("unchecked")
private void nestMap(String prefix, Map<String, Object> result, Map<String, Object> map) {
if (prefix.length() > 0) {
prefix += ".";
}
for (Map.Entry<String, Object> entrySet : map.entrySet()) {
if (entrySet.getValue() instanceof Map) {
nestMap(prefix + entrySet.getKey(), result, (Map<String, Object>) entrySet.getValue());
} else {
result.put(prefix + entrySet.getKey().toString(), entrySet.getValue());
}
}
}
}
spring.factories配置:
# PropertySource Loaders org.springframework.boot.env.PropertySourceLoader=\ zhong.test.springbootdemo.usultestdemo.configration.propertiespropertysource.myconfiguretation.MyPropertySourceLoader
在spring.factories文件所在的resources文件添加appllcation.json文件
{ "key1": "value1", "key2": "value2" }
测试代码:
@RestController
@SpringBootApplication(scanBasePackages = "zhong.test.springbootdemo.usultestdemo")
public class UsulTestStartApplication implements CommandLineRunner {
@Value("${key1}")
String value;
@Override
public void run(String... args) throws Exception {
System.out.println("key = " + value);
}
}
结果显示: key = value1。
如果我们在启动类所在的resources中增加 applcation.json文件 和 在application.properties中加入相同配置。测试引用的jar的配置、项目的jar的优先级、application.properties配置的优先级。通过断点显示 优先级 :启动类所在的resources中的 applcation.json > jar所在的resourcs的 applcation.json > application.properties配置。
org.springframework.cloud.bootstrap.BootstrapConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration是对org.springframework.cloud.bootstrap.config.PropertySourceLocator 的实现类实现的配置的注解。和PropertySourceLoade类似,只不过不是针对特定的applcation文集解析。是可以有我们自定义解析配置的文件。我们可以定义恁地的配置文件,也可以实现从远程调用获取的方式实现。很适合springboot自定义开发。
依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-context</artifactId> <version>2.1.0.RELEASE</version> </dependency>
实现类:
public class ConfigLocalPropertySourceLocator implements PropertySourceLocator {
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigLocalPropertySourceLocator.class);
public PropertySource<?> locate(Environment environment) {
//记录最终加载到运行环境中的数据
Properties localConfig = new Properties();
localConfig.put("PropertySourceLocator-key", "PropertySourceLocator-value");
return new PropertiesPropertySource("localConfig", localConfig);
}
spring.factories配置
第一种配置方式,直接配置spring.factories
:
# Bootstrap components org.springframework.cloud.bootstrap.BootstrapConfiguration=\ zhong.test.springbootdemo.usultestdemo.configration.propertiespropertysource.ConfigLocalPropertySourceLocator
第二种配置方式
通过@Configuration和@Bean实现。可以附加很多的控制条件。
@Configuration
public class ConfigLocalConfiguration {
public ConfigLocalConfiguration() {
}
@Bean
@ConditionalOnMissingBean({ConfigLocalPropertySourceLocator.class})
public ConfigLocalPropertySourceLocator configLocalPropertySourceLocator() {
return new ConfigLocalPropertySourceLocator();
}
}
spring.factories配置:
# Bootstrap components org.springframework.cloud.bootstrap.BootstrapConfiguration=\ zhong.test.springbootdemo.usultestdemo.configration.propertiespropertysource.ConfigLocalConfiguration
测试测试代码:
@SpringBootApplication(scanBasePackages = "zhong.test.springbootdemo.usultestdemo")
public class UsulTestStartApplication implements CommandLineRunner {
@Value("${PropertySourceLocator-key}")
String value2;
@Override
public void run(String... args) throws Exception {
System.out.println("PropertySourceLocator-key = " + value2);
}
}
结果为控制台打印 PropertySourceLocator-key = PropertySourceLocator-value。
同时 在application.properties中加入相同配置测试配置的顺序。结果显示,优先级: ConfigLocalPropertySourceLocator > application.properties中相同配置。
在spingboot的自定义开发中可以使用这个配置来实现自定义配置。甚至实现类似spring-cloud-config的功能,来达到统一配置管理。
任务类加载
任务类的key为: org.springframework.boot.autoconfigure.EnableAutoConfiguration。value可以是CommandLineRunner、DisposableBean、ApplicationContextAware、InitializingBean接口的实现,这样会在项目启动的时候执行初始化任务、或者项目结束的时候执行销毁方法。
配置方式都有两种方式。
一种是直接在spring.factories中直接配合着实现类:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
zhong.test.springbootdemo.usultestdemo.configration.propertiespropertysource.myconfiguretation.MyCommandRunable,\
zhong.test.springbootdemo.usultestdemo.configration.propertiespropertysource.myconfiguretation.MyDisposableBeanConfigureration,\
zhong.test.springbootdemo.usultestdemo.configration.propertiespropertysource.myconfiguretation.MyInitBeanConfigureration,\
zhong.test.springbootdemo.usultestdemo.configration.propertiespropertysource.myconfiguretation.MyApplicationContextAware
第二种是通过@Configuration和@Bean实现。可以附加很多的控制条件。
@Configuration
public class ConfigLocalConfiguration {
public ConfigLocalConfiguration() {
}
@Bean
@ConditionalOnMissingBean({MyCommandRunable.class})
public MyCommandRunable configLocalPropertySourceLocator() {
return new MyCommandRunable();
}
}
控制条件有很多:
spring.factories文件里每一个xxxAutoConfiguration文件一般都会有下面的条件注解:
- @ConditionalOnClass : classpath中存在该类时起效
- @ConditionalOnMissingClass : classpath中不存在该类时起效
- @ConditionalOnBean : DI容器中存在该类型Bean时起效
- @ConditionalOnMissingBean : DI容器中不存在该类型Bean时起效
- @ConditionalOnSingleCandidate : DI容器中该类型Bean只有一个或@Primary的只有一个时起效
- @ConditionalOnExpression : SpEL表达式结果为true时
- @ConditionalOnProperty : 参数设置或者值一致时起效
- @ConditionalOnResource : 指定的文件存在时起效
- @ConditionalOnJndi : 指定的JNDI存在时起效
- @ConditionalOnJava : 指定的Java版本存在时起效
- @ConditionalOnWebApplication : Web应用环境下起效
- @ConditionalOnNotWebApplication : 非Web应用环境下起效
我们可以根据自己需要实现即可。
public class MyApplicationContextAware implements ApplicationContextAware {
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("MyApplicationContextAware ------ ApplicationContextAware");
}
}
public class MyCommandRunable implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("MyCommandRunable ------ CommandLineRunner");
}
}
public class MyDisposableBeanConfigureration implements DisposableBean {
@Override
public void destroy() throws Exception {
System.out.println("MyDisposableBeanConfigration ------ DisposableBean");
}
}
public class MyInitBeanConfigureration implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("MyInitBeanConfigureration ------ InitializingBean");
}
}
其实, 在springboot的jar中我们可以看到,springboot本身也使用了很多配置。
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
# Error Reporters
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor
# Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindValidationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.UnboundConfigurationPropertyFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer
# FailureAnalysisReporters
org.springframework.boot.diagnostics.FailureAnalysisReporter=\
org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter