在之前的文章中已经大致解释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的实现类实现的自定义配置加载过程。
类实现:
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 配置。
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.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文件一般都会有下面的条件注解:
我们可以根据自己需要实现即可。
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