http://www.jianshu.com/p/5206f74a4406

*********************************

前言

上一篇文章介绍了SpringBoot的EnableAutoConfiguration,并通过自定义注解来实现相同的功能。
这里再介绍一下SpringBoot的配置文件的加载机制,SpringBoot会默认加载ClassPath下的application.properties的文件,下面会介绍实现的原理,并通过自定义PropertySourceLoader来自定义配置加载。

PropertySourceLoader

SpringBoot加载配置文件的入口是ConfigFileApplicationListener,这个类实现了ApplicationListenerEnvironmentPostProcessor两个接口。
SpringApplication在初始化的时候会加载spring.factories配置的ApplicationListener接口的实现类。

    private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
this.webEnvironment = deduceWebEnvironment();
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}

ConfigFileApplicationListener包含了PropertySourcesLoader,这个类会从spring.factories加载PropertySourceLoader的实现类。

    public PropertySourcesLoader(MutablePropertySources propertySources) {
Assert.notNull(propertySources, "PropertySources must not be null");
this.propertySources = propertySources;
this.loaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,getClass().getClassLoader());
}

PropertySourceLoader就是加载配置接口类。

    public interface PropertySourceLoader {
String[] getFileExtensions();
PropertySource<?> load(String name, Resource resource, String profile)
throws IOException;
}

getFileExtensions()是返回支持的文件扩展名,比如PropertiesPropertySourceLoader支持的扩展是xmlproperties
ConfigFileApplicationListener定义了默认的文件名DEFAULT_NAMES="application",所以SpringBoot会根据文件名加扩展名来加载文件。
load方法会读取配置文件,并返回PropertySource,SpringBoot会从PropertySource读取配置项,合并到总的配置对象中。

自定义PropertySourceLoader

所以自定义PropertySourceLoader就需要实现接口类,并配置到spring.factories中。
SpringBoot没有加载json的配置文件,这里就自定义JsonPropertySourceLoader来实现json格式配置文件的加载。完整的代码放在Github

  1. 定义JsonPropertySourceLoader,这里返回json的扩展名,通过SpringBoot内置的JsonParse,解析文件。
    解析成map格式,然后根据json的层级结构,递归进去,拼接成完整的key。

     public class JsonPropertySourceLoader implements PropertySourceLoader {
    public String[] getFileExtensions() {
    return new String[]{"json"};
    }
    public PropertySource<?> load(String name, Resource resource, String profile) throws IOException {
    Map<String, Object> result = mapPropertySource(resource);
    return new MapPropertySource(name, result);
    }
    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);
    }
    private void nestMap(String prefix, Map<String, Object> result, Map<String, Object> map) {
    if (prefix.length() > 0) {
    prefix += ".";
    }
    for (Map.Entry 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());
    }
    }
    }
    }
  2. 配置文件,这里配置了customize.property.message和日志级别logging.level.root。
    {
    "customize": {
    "property": {
    "message": "hello world"
    }
    },
    "logging": {
    "level": {
    "root": "ERROR"
    }
    }
    }
  3. 配置PropertySourceLoader,在工程下新建/META-INF/spring.factories文件,并配置JsonPropertySourceLoader
    org.springframework.boot.env.PropertySourceLoader=org.wcong.test.springboot.JsonPropertySourceLoader
  4. main入口,取出customize.property.message并打印出来。
     @Configuration
    @EnableAutoConfiguration
    public class CustomizePropertySourceLoader {
    @Value("${customize.property.message}")
    private String message;
    public static void main(String[] args) {
    SpringApplication springApplication = new SpringApplication(CustomizePropertySourceLoader.class);
    springApplication.setWebEnvironment(false);
    ConfigurableApplicationContext configurableApplicationContext = springApplication.run(args);
    CustomizePropertySourceLoader customizePropertySourceLoader = configurableApplicationContext.getBean(CustomizePropertySourceLoader.class);
    System.out.println(customizePropertySourceLoader.message);
    }
    }

结语

SpringBoot通过spring.factories实现了很好的扩展功能。自定义模块相关一般是通过实现对应的接口,并配置到文件中。后面会介绍更多关于SpringBoot的内容。

文/wcong(简书作者)
原文链接:http://www.jianshu.com/p/5206f74a4406
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
 

深入SpringBoot:自定义PropertySourceLoader的更多相关文章

  1. 深入SpringBoot:自定义Endpoint

    前言 上一篇文章介绍了SpringBoot的PropertySourceLoader,自定义了Json格式的配置文件加载.这里再介绍下EndPoint,并通过自定EndPoint来介绍实现原理. En ...

  2. SpringBoot自定义拦截器实现IP白名单功能

    SpringBoot自定义拦截器实现IP白名单功能 转载请注明源地址:http://www.cnblogs.com/funnyzpc/p/8993331.html 首先,相关功能已经上线了,且先让我先 ...

  3. SpringBoot自定义错误信息,SpringBoot适配Ajax请求

    SpringBoot自定义错误信息,SpringBoot自定义异常处理类, SpringBoot异常结果处理适配页面及Ajax请求, SpringBoot适配Ajax请求 ============== ...

  4. SpringBoot自定义错误页面,SpringBoot 404、500错误提示页面

    SpringBoot自定义错误页面,SpringBoot 404.500错误提示页面 SpringBoot 4xx.html.5xx.html错误提示页面 ====================== ...

  5. springboot自定义错误页面

    springboot自定义错误页面 1.加入配置: @Bean public EmbeddedServletContainerCustomizer containerCustomizer() { re ...

  6. SpringBoot自定义Filter

    SpringBoot自定义Filter SpringBoot自动添加了OrderedCharacterEncodingFilter和HiddenHttpMethodFilter,当然我们可以自定 义F ...

  7. springboot 自定义LocaleResolver切换语言

    springboot 自定义LocaleResolver切换语言 我们在做项目的时候,往往有很多项目需要根据用户的需要来切换不同的语言,使用国际化就可以轻松解决. 我们可以自定义springboor中 ...

  8. SpringMVC拦截器与SpringBoot自定义拦截器

    首先我们先回顾一下传统拦截器的写法: 第一步创建一个类实现HandlerInterceptor接口,重写接口的方法. 第二步在XML中进行如下配置,就可以实现自定义拦截器了 SpringBoot实现自 ...

  9. [技术博客] SPRINGBOOT自定义注解

    SPRINGBOOT自定义注解 在springboot中,有各种各样的注解,这些注解能够简化我们的配置,提高开发效率.一般来说,springboot提供的注解已经佷丰富了,但如果我们想针对某个特定情景 ...

随机推荐

  1. C/C++相对论——C++中为什么要使用异常(跳转语句会造成对象没有被析构)

    C++中为什么要使用异常? 很多人也许知道C++中的异常机制,很多人也许不知道.很多人知道C中常用的assert,也知道在编译时候指定NODEBUG来忽略它. 对于C语言,使用正常的if-else即是 ...

  2. python 安装easy_install和pip

    做个记录 easy_install和pip都是用来下载安装Python一个公共资源库PyPI的相关资源包的 首先安装easy_install 下载地址:https://pypi.python.org/ ...

  3. 微软自带的Serialization和Newtonsoft简单测试

    刚刚对这两个进行了一下小小的测试 发现 当转换的内容少的时候  微软自带的比Newtonsoft速度要快一些,内容多的时候反之,当内容多到一定量的时候微软自带的就不能转换了,需要修改一下MaxJson ...

  4. zw版·全程图解Halcon控件安装(delphi2007版)

    zw版·全程图解Halcon控件安装(delphi2007版) delphi+halcon,这个组合,可以说是图像分析的神级配置,无论是开发效率,还是运行实在是太高了,分分钟秒杀c+opencv,py ...

  5. JSTL函数

    JSTL包含了一系列标准函数. 引入:<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functi ...

  6. hibernate的二级缓存

    缓存(Cache): 计算机领域非常通用的概念.它介于应用程序和永久性数据存储源(如硬盘上的文件或者数据库)之间,其作用是降低应用程序直接读写永久性数据存储源的频率,从而提高应用的运行性能.缓存中的数 ...

  7. Sublime Text怎么在切分两行视口内显示同一个文件

    原文链接:http://devlog.en.alt-area.org/?p=1098 How to split one file into two views in Sublime Text2 You ...

  8. OWASP top10

    PhishTank 是互联网上免费提供恶意网址黑名单的组织之一,它的黑名单由世界各地的志愿者提供,且更新频繁. 1.XSS 1.1. XSS简介 跨站脚本攻击,英文全称是Cross Site Scri ...

  9. filebeat安装与基础用法

    来自官网,版本为1.2 下载rpm包并安装 wget -c https://download.elastic.co/beats/filebeat/filebeat-1.2.3-x86_64.rpm r ...

  10. 思科Cisco 2960系列交换机配置命令

    配置密码: 2960>en :第一次密码为空 2960h#conf t :进入全局配置模式 2960(config)#hostname swa :设置交换机名 2960(config)#enab ...