美文网首页Java编程
SpringBoot系列之@PropertySource读取ya

SpringBoot系列之@PropertySource读取ya

作者: smileNicky | 来源:发表于2019-11-26 15:50 被阅读0次

    SpringBoot系列之@PropertySource支持yaml文件读取

    最近在做实验,想通过@PropertySource注解读取配置文件的属性,进行映射,习惯上用properties都是测试没问题的,偶然换成yaml文件,发现都读取不到属性值

    因为yaml语法很简洁,比较喜欢写yaml配置文件,很显然,@PropertySource默认不支持yaml读取,我们改成@Value注解也是可以读取的,不过属性一堆的话,一个一个读取也是很繁琐的,通过网上找资料和自己实验验证,发现是可以实现对yaml支持

    然后,为什么@PropertySource注解默认不支持?可以简单跟一下源码

    @PropertySource源码:


    在这里插入图片描述

    根据注释,默认使用DefaultPropertySourceFactory类作为资源文件加载类


    在这里插入图片描述
    里面还是调用Spring框架底层的PropertiesLoaderUtils工具类进行读取的
    在这里插入图片描述

    PropertiesLoaderUtils.loadProperties


    在这里插入图片描述
    从源码可以看出也是支持xml文件读取的,能支持reader就获取reader对象,否则出件inputStream
    在这里插入图片描述 在这里插入图片描述

    load0方法是关键,这里加了同步锁


    在这里插入图片描述

    很重要的load0 方法抓取出来:

    private void load0 (LineReader lr) throws IOException {
            char[] convtBuf = new char[1024];
            int limit;
            // 当前key所在位置
            int keyLen;
            // 当前value所在位置
            int valueStart;
            char c;//读取的字符
            boolean hasSep;
            boolean precedingBackslash;//是否转义字符,eg:/n etc.
            // 一行一行地读取
            while ((limit = lr.readLine()) >= 0) {
                c = 0;
                keyLen = 0;
                valueStart = limit;
                hasSep = false;
    
                //System.out.println("line=<" + new String(lineBuf, 0, limit) + ">");
                precedingBackslash = false;
                //key的长度小于总的字符长度,那么就进入循环
                while (keyLen < limit) {
                    c = lr.lineBuf[keyLen];
                    //need check if escaped.
                    if ((c == '=' ||  c == ':') && !precedingBackslash) {
                        valueStart = keyLen + 1;
                        hasSep = true;
                        break;
                    } else if ((c == ' ' || c == '\t' ||  c == '\f') && !precedingBackslash) {
                        valueStart = keyLen + 1;
                        break;
                    }
                    if (c == '\\') {
                        precedingBackslash = !precedingBackslash;
                    } else {
                        precedingBackslash = false;
                    }
                    keyLen++;
                }
                //value的起始位置小于总的字符长度,那么就进入该循环
                while (valueStart < limit) {
                    c = lr.lineBuf[valueStart];
                    //当前字符是否非空格类字符
                    if (c != ' ' && c != '\t' &&  c != '\f') {
                        if (!hasSep && (c == '=' ||  c == ':')) {
                            hasSep = true;
                        } else {
                            break;
                        }
                    }
                    valueStart++;
                }
                //读取key
                String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf);
                //读取value
                String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf);
                put(key, value);
            }
        }
    

    ok,从源码可以看出,这个方法是一行一行地读取,然后根据冒号、等于号、空格等进行校验,经过一系列遍历之后获取key和value,而yaml语法是以缩进来辨别的,经过自己调试,这个方法也是不支持yaml文件的读取的,properties源码是比较多的,具体的Properties源码实现的可以参考博客:https://www.cnblogs.com/liuming1992/p/4360310.html,这篇博客写的比较详细

    ok,然后给个例子来实现对yaml配置文件的读取

    # 测试ConfigurationProperties
    user:
      userName: root
      isAdmin: true
      regTime: 2019/11/01
      isOnline: 1
      maps: {k1 : v1,k2: v2}
      lists:
       - list1
       - list2
      address:
        tel: 15899988899
        name: 上海市
    
    

    模仿DefaultPropertySourceFactory写一个yaml资源文件读取的工厂类:

    package com.example.springboot.properties.core.propertyResouceFactory;
    
    import org.springframework.boot.env.YamlPropertySourceLoader;
    import org.springframework.core.env.PropertiesPropertySource;
    import org.springframework.core.env.PropertySource;
    import org.springframework.core.io.support.EncodedResource;
    import org.springframework.core.io.support.PropertySourceFactory;
    import org.springframework.lang.Nullable;
    
    import java.io.IOException;
    import java.util.List;
    import java.util.Optional;
    import java.util.Properties;
    
    /**
     * <pre>
     *  YAML配置文件读取工厂类
     * </pre>
     * <p>
     * <pre>
     * @author nicky.ma
     * 修改记录
     *    修改后版本:     修改人:  修改日期: 2019/11/13 15:44  修改内容:
     * </pre>
     */
    public class YamlPropertyResourceFactory implements PropertySourceFactory {
    
        /**
         * Create a {@link PropertySource} that wraps the given resource.
         *
         * @param name     the name of the property source
         * @param encodedResource the resource (potentially encoded) to wrap
         * @return the new {@link PropertySource} (never {@code null})
         * @throws IOException if resource resolution failed
         */
        @Override
        public PropertySource<?> createPropertySource(@Nullable String name, EncodedResource encodedResource) throws IOException {
            String resourceName = Optional.ofNullable(name).orElse(encodedResource.getResource().getFilename());
            if (resourceName.endsWith(".yml") || resourceName.endsWith(".yaml")) {//yaml资源文件
                List<PropertySource<?>> yamlSources = new YamlPropertySourceLoader().load(resourceName, encodedResource.getResource());
                return yamlSources.get(0);
            } else {//返回空的Properties
                return new PropertiesPropertySource(resourceName, new Properties());
            }
        }
    }
    
    

    写个bean类进行属性映射,注意换一下factory参数,factory = YamlPropertyResourceFactory.class

    package com.example.springboot.properties.bean;
    
    import com.example.springboot.properties.core.propertyResouceFactory.CommPropertyResourceFactory;
    import com.example.springboot.properties.core.propertyResouceFactory.YamlPropertyResourceFactory;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.PropertySource;
    import org.springframework.core.io.support.DefaultPropertySourceFactory;
    import org.springframework.stereotype.Component;
    
    import java.util.Date;
    import java.util.List;
    import java.util.Map;
    
    /**
     * <pre>
     *
     * </pre>
     *
     * @author nicky
     * <pre>
     * 修改记录
     *    修改后版本:     修改人:  修改日期: 2019年11月03日  修改内容:
     * </pre>
     */
    @Component
    @PropertySource(value = "classpath:user.yml",encoding = "utf-8",factory = YamlPropertyResourceFactory.class)
    @ConfigurationProperties(prefix = "user")
    public class User {
    
        private String userName;
        private boolean isAdmin;
        private Date regTime;
        private Long isOnline;
        private Map<String,Object> maps;
        private List<Object> lists;
        private Address address;
    
        @Override
        public String toString() {
            return "User{" +
                           "userName='" + userName + '\'' +
                           ", isAdmin=" + isAdmin +
                           ", regTime=" + regTime +
                           ", isOnline=" + isOnline +
                           ", maps=" + maps +
                           ", lists=" + lists +
                           ", address=" + address +
                           '}';
        }
    
        public String getUserName() {
            return userName;
        }
    
        public void setUserName(String userName) {
            this.userName = userName;
        }
    
        public boolean isAdmin() {
            return isAdmin;
        }
    
        public void setAdmin(boolean admin) {
            isAdmin = admin;
        }
    
        public Date getRegTime() {
            return regTime;
        }
    
        public void setRegTime(Date regTime) {
            this.regTime = regTime;
        }
    
        public Long getIsOnline() {
            return isOnline;
        }
    
        public void setIsOnline(Long isOnline) {
            this.isOnline = isOnline;
        }
    
        public Map<String, Object> getMaps() {
            return maps;
        }
    
        public void setMaps(Map<String, Object> maps) {
            this.maps = maps;
        }
    
        public List<Object> getLists() {
            return lists;
        }
    
        public void setLists(List<Object> lists) {
            this.lists = lists;
        }
    
        public Address getAddress() {
            return address;
        }
    
        public void setAddress(Address address) {
            this.address = address;
        }
    }
    
    
    
    package com.example.springboot.properties.bean;
    
    /**
     * <pre>
     *
     * </pre>
     *
     * @author nicky
     * <pre>
     * 修改记录
     *    修改后版本:     修改人:  修改日期: 2019年11月03日  修改内容:
     * </pre>
     */
    public class Address {
        private String tel;
        private String name;
    
        @Override
        public String toString() {
            return "Address{" +
                           "tel='" + tel + '\'' +
                           ", name='" + name + '\'' +
                           '}';
        }
    
        public String getTel() {
            return tel;
        }
    
        public void setTel(String tel) {
            this.tel = tel;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    
    

    junit测试类代码:

    package com.example.springboot.properties;
    
    import com.example.springboot.properties.bean.User;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    
    @SpringBootTest
    class SpringbootPropertiesConfigApplicationTests {
    
        @Autowired
        User user;
    
        @Test
        public void testConfigurationProperties(){
            System.out.println(user);
        }
    
    
    }
    
    
    User{userName='root(15899988899)', isAdmin=false, regTime=Fri Nov 01 00:00:00 SGT 2019, isOnline=1, maps={k2=v2, k1=-30363940}, lists=[1f90e323-8a9c-4194-a31c-be9abbe9ce38, a869f68947faa92964d2a36ce86ee980], address=Address{tel='15899988899', name='上海浦东区'}}
    

    如果既要支持原来的yaml,又要支持properties,就可以将propertyResourceFactory类进行改写一下:

    package com.example.springboot.properties.core.propertyResouceFactory;
    
    import org.springframework.boot.env.YamlPropertySourceLoader;
    import org.springframework.core.env.PropertySource;
    import org.springframework.core.io.support.DefaultPropertySourceFactory;
    import org.springframework.core.io.support.EncodedResource;
    import org.springframework.core.io.support.PropertySourceFactory;
    import org.springframework.lang.Nullable;
    
    import java.io.IOException;
    import java.util.List;
    import java.util.Optional;
    
    /**
     * <pre>
     *  通用的资源文件读取工厂类
     * </pre>
     * <p>
     * <pre>
     * @author mazq
     * 修改记录
     *    修改后版本:     修改人:  修改日期: 2019/11/25 10:35  修改内容:
     * </pre>
     */
    public class CommPropertyResourceFactory implements PropertySourceFactory {
    
        /**
         * Create a {@link PropertySource} that wraps the given resource.
         *
         * @param name     the name of the property source
         * @param resource the resource (potentially encoded) to wrap
         * @return the new {@link PropertySource} (never {@code null})
         * @throws IOException if resource resolution failed
         */
        @Override
        public PropertySource<?> createPropertySource(@Nullable String name, EncodedResource resource) throws IOException {
            String resourceName = Optional.ofNullable(name).orElse(resource.getResource().getFilename());
            if (resourceName.endsWith(".yml") || resourceName.endsWith(".yaml")) {
                List<PropertySource<?>> yamlSources = new YamlPropertySourceLoader().load(resourceName, resource.getResource());
                return yamlSources.get(0);
            } else {
                return new DefaultPropertySourceFactory().createPropertySource(name, resource);
            }
        }
    }
    
    

    调用的时候,要改一下factory参数

    @PropertySource(value = "classpath:user.yml",encoding = "utf-8",factory = CommPropertyResourceFactory.class)
    

    这个类就可以支持原来的properties文件,也可以支持yaml文件

    User{userName='root(15899988899)', isAdmin=false, regTime=Fri Nov 01 00:00:00 SGT 2019, isOnline=1, maps={k2=v2, k1=-30363940}, lists=[1f90e323-8a9c-4194-a31c-be9abbe9ce38, a869f68947faa92964d2a36ce86ee980], address=Address{tel='15899988899', name='上海浦东区'}}
    

    代码下载:github下载链接

    相关文章

      网友评论

        本文标题:SpringBoot系列之@PropertySource读取ya

        本文链接:https://www.haomeiwen.com/subject/udxywctx.html