210902-实战小技巧16:Properties配置文件自动装载JavaBean

文章目录
  1. 1. 配置文件自动装载
  2. 2. 功能测试
  • II. 其他
    1. 1. 一灰灰Blog: https://liuyueyi.github.io/hexblog
    2. 2. 声明
    3. 3. 扫描关注
  • 每天一个实战小技巧,Properties配置文件自动装载JavaBean

    SpringBoot的配置自动装载,使用起来还是很舒爽的,可以非常简单的将properties配置文件的内容,填充到Java bean对象中,如果我们现在是一个脱离于Springboot框架的项目,想实现上面这个功能,可以怎么来做呢?

    1. 配置文件自动装载

    前面介绍了Properties文件的读取以及基本使用姿势,通过上篇博文已知Properties类的本质是一个Map,所以我们需要干的就是将Map容器的值,赋值到JavaBean的成员属性中

    要实现这个功能,自然而然会想到的就是利用反射(考虑到我们赋值的通常为标准的java bean,使用内省是个更好的选择)

    接下来我们需要实现的也比较清晰了,第一步获取成员属性,两种方式

    • 内省: BeanInfo bean = Introspector.getBeanInfo(clz); PropertyDescriptor[] propertyDescriptors = bean.getPropertyDescriptors();
    • 反射: Field[] fields = clz.getDeclaredFields();

    第二步遍历成员属性,进行赋值

    • 内省:借助前面获取的PropertyDescriptor对象,拿到set方法,进行赋值
      • descriptor.getWriteMethod().invoke(obj, value)
    • 反射:适应Field.set来赋值
      • field.set(obj, value);

    注意

    • 上面的两种赋值方式,都要求我们传入的value对象类型与定义类型一直,否则会抛类型转换异常

    为了避免复杂的类型转换与判定,我们这里介绍下apache的commons-beanutils来实现属性拷贝

    1
    2
    3
    4
    5
    6
    <!-- https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils -->
    <dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.9.4</version>
    </dependency>

    接下来核心的实现逻辑如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    private static boolean isPrimitive(Class clz) {
    if (clz.isPrimitive()) {
    return true;
    }

    try {
    return ((Class) clz.getField("TYPE").get(null)).isPrimitive();
    } catch (Exception e) {
    return false;
    }
    }

    public static <T> T toBean(Properties properties, Class<T> type, String prefix) throws IntrospectionException, IllegalAccessException, InstantiationException, InvocationTargetException {
    if (prefix == null) {
    prefix = "";
    } else if (!prefix.isEmpty() && !prefix.endsWith(".")) {
    prefix += ".";
    }

    type.getDeclaredFields();

    // 内省方式来初始化
    T obj = type.newInstance();
    BeanInfo bean = Introspector.getBeanInfo(type);
    PropertyDescriptor[] propertyDescriptors = bean.getPropertyDescriptors();
    for (PropertyDescriptor descriptor : propertyDescriptors) {
    // 只支持基本数据类型的拷贝
    Class fieldType = descriptor.getPropertyType();
    if (fieldType == Class.class) {
    continue;
    }

    if (isPrimitive(fieldType) || fieldType == String.class) {
    // 支持基本类型的转换,如果使用 PropertyUtils, 则不会实现基本类型 + String的自动转换
    BeanUtils.setProperty(obj, descriptor.getName(), properties.getProperty(prefix + descriptor.getName()));
    } else {
    BeanUtils.setProperty(obj, descriptor.getName(), toBean(properties, fieldType, prefix + descriptor.getName()));
    }
    }
    return obj;
    }

    注意上面的实现,首先通过内省的方式获取所有的成员,然后进行遍历,借助BeanUtils.setProperty来实现属性值设置

    这里面有两个知识点

    • BeanUtil 还是 PropertyUtil
      • 它们两都有个设置属性的方法,但是BeanUtil支持简单类型的自动转换;而后者不行,要求类型完全一致
    • 非简单类型
      • 对于非简单类型,上面采用了递归的调用方式来处理;请注意,这里并不完善,比如BigDecimal, Date, List, Map这些相对基础的类型,是不太适用的哦

    2. 功能测试

    最后针对上面的实现功能,简单的测试一下,是否可行

    配置文件mail.properties

    1
    2
    3
    4
    5
    6
    7
    mail.host=localhost
    mail.port=25
    mail.smtp.auth=false
    mail.smtp.starttlsEnable=false
    mail.from=test@yhhblog.com
    mail.username=user
    mail.password=pwd

    两个Java Bean

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @Data
    public static class MailProperties {
    private String host;
    private Integer port;
    private Smtp smtp;
    private String from;
    private String username;
    private String password;
    }

    @Data
    public static class Smtp {
    private String auth;
    private String starttlsEnable;
    }

    转换测试类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public static Properties loadProperties(String propertyFile) throws IOException {
    Properties config = new Properties();
    config.load(PropertiesUtil.class.getClassLoader().getResourceAsStream(propertyFile));
    return config;
    }

    @Test
    public void testParse() throws Exception {
    Properties properties = loadProperties("mail.properties");
    MailProperties mailProperties = toBean(properties, MailProperties.class, "mail");
    System.out.println(mailProperties);
    }

    输出结果如下:

    1
    PropertiesUtil.MailProperties(host=localhost, port=25, smtp=PropertiesUtil.Smtp(auth=false, starttlsEnable=false), from=test@yhhblog.com, username=user, password=pwd)

    系列博文:

    II. 其他

    1. 一灰灰Bloghttps://liuyueyi.github.io/hexblog

    一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

    2. 声明

    尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

    3. 扫描关注

    一灰灰blog

    QrCode

    评论

    Your browser is out-of-date!

    Update your browser to view this website correctly. Update my browser now

    ×