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

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

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来实现属性拷贝


<!-- https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils -->
<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.9.4</version>
</dependency>
复制代码


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


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

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

@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;
}
复制代码


转换测试类

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);
}
复制代码


输出结果如下:

PropertiesUtil.MailProperties(host=localhost, port=25, smtp=PropertiesUtil.Smtp(auth=false,



相关文章
CKEditor5 支持 WPS 贴贴文字图片,默认贴贴进入空白空格
CKEditor5 支持 WPS 贴贴文字图片,默认贴贴进入空白空格
856 0
|
安全 Linux 数据安全/隐私保护
驾驭Linux的权力:Root与Sudo
在 Linux 系统中,权限管理至关重要,Root 用户与 Sudo 命令为核心组件。Root 作为超级用户,拥有最高权限,可执行任意命令,但也带来较高安全风险,建议仅在必要时使用。Sudo 则允许系统管理员授予普通用户临时的 Root 权限以执行特定命令,提升了系统的安全性和管理灵活性。通过合理配置 Sudoers 文件,可以实现对用户权限的精细化管理。综合运用 Root 和 Sudo 可确保系统的安全稳定运行。
909 1
|
Java 开发者 Spring
面向切面编程(SpringAOP)、通过注解实现AOP代码、AOP的工作流程
面向切面编程(SpringAOP)、通过注解实现AOP代码、AOP的工作流程
470 1
面向切面编程(SpringAOP)、通过注解实现AOP代码、AOP的工作流程
|
SQL Oracle 关系型数据库
SQL查询结果导出方法详析及实践指南
导出SQL查询结果是数据库管理中的一个重要环节。不同的数据库系统提供了各自的工具和方法来完成这项任务。选择合适的方法取决于具体的应用场景、数据规模和个人偏好。无论是在命令行中使用简单的SQL语句,还是通过GUI工具或编程语言实现自动化脚本,都有助于提高工作效率,简化数据管理流程。
|
机器学习/深度学习 自然语言处理
预训练-微调范式
预训练-微调范式
|
SQL Java 数据库连接
JDBCTEMPLATE 的基本使用----查询操作26
JDBCTEMPLATE 的基本使用----查询操作26
JDBCTEMPLATE 的基本使用----查询操作26
|
存储 JavaScript 前端开发
Spring Boot + Vue: 实现文件导入导出功能
本文介绍了使用Spring Boot和Vue实现文件导入导出的步骤。在后端,Spring Boot通过`MultipartFile`接收上传文件,保存至服务器,并使用`ResponseEntity`提供文件下载。前端部分,Vue项目借助`axios`发送HTTP请求,实现文件选择、上传及下载功能。这种前后端分离的实现方式提高了应用的可维护性和可扩展性。
1659 2
|
存储 Kubernetes 安全
加密 K8s Secrets 的几种方案
加密 K8s Secrets 的几种方案
|
存储 分布式计算 Hadoop
hudi的安装部署以及基本使用方法
hudi的安装部署以及基本使用方法