• 深入SpringBoot:自定义PropertySourceLoader


    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
    著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
     
  • 相关阅读:
    Maven关于web.xml中Servlet和Servlet映射的问题
    intellij idea的Maven项目运行报程序包找不到的错误
    修改Maven项目默认JDK版本
    刷题15. 3Sum
    刷题11. Container With Most Water
    刷题10. Regular Expression Matching
    刷题5. Longest Palindromic Substring
    刷题4. Median of Two Sorted Arrays
    刷题3. Longest Substring Without Repeating Characters
    刷题2. Add Two Numbers
  • 原文地址:https://www.cnblogs.com/zhao1949/p/6226288.html
Copyright © 2020-2023  润新知