META-INF/spring.factories使用测试

在之前的文章中已经大致解释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

上一篇:Floyd——人人都是中间商(50%)


下一篇:【Codeforce Round 748 D2】