在之前的文章中已经大致解释META-INF/spring.factories的作用以及加载流程,本章项目需要实现一些示例一下示例。
配置类加载
首先是配置加载实现的加载实现:
实现方式有三种:
- org.springframework.cloud.bootstrap.BootstrapConfiguration:
表示org.springframework.cloud.bootstrap.config.PropertySourceLocator 的实现类实现的配置加载过程。
2. org.springframework.boot.env.PropertySourceLoade :
表示org.springframework.boot.env.PropertySourceLoader的实现类实现的解析application配置文件。
3. org.springframework.boot.env.EnvironmentPostProcessor :
表示org.springframework.boot.env.EnvironmentPostProcessor的实现类实现的自定义配置加载过程。
org.springframework.boot.env.EnvironmentPostProcessor
类实现:
public class MyEnvironmentPostProcessor implements EnvironmentPostProcessor {@Overridepublic 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;@Overridepublic 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*/@Overridepublic String[] getFileExtensions() {return new String[]{"json"};}@Overridepublic 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;@Overridepublic 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;@Overridepublic 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 {@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {System.out.println("MyApplicationContextAware ------ ApplicationContextAware");}
}
public class MyCommandRunable implements CommandLineRunner {@Overridepublic void run(String... args) throws Exception {System.out.println("MyCommandRunable ------ CommandLineRunner");}
}
public class MyDisposableBeanConfigureration implements DisposableBean {@Overridepublic void destroy() throws Exception {System.out.println("MyDisposableBeanConfigration ------ DisposableBean");}
}
public class MyInitBeanConfigureration implements InitializingBean {@Overridepublic 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