栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

SpringBoot通过自定义字段注解以及反射获取对象

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SpringBoot通过自定义字段注解以及反射获取对象

在Java的开发过程中,注解的应用场景是非常广泛的。Java也提供了很多内置的注解,比如@Override,@Deprecated,@SuppressWarnings等等。之前也写过一篇注解相关的文章,SpringBoot自定义注解 AOP以及拦截器方式。本文主要介绍通过自定义字段注解以及反射,实现初始化对象的功能。应用场景主要是通过外部接口,数据库,文本或者Excel读取数据,然后通过反射以及字段注解自动转换为对象,灵活的处理外部数据到对象的转换。

自定义字段注解

定义自定义字段注解DbFieldProperty,value表示字段注解的值,name为字段注解名称,clazz为类型。

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@documented
@Inherited
public @interface DbFieldProperty {
    String value() default "";

    String name() default "";

    Class clazz() default Object.class;
}
使用自定义注解

在AutoUser类中使用我们自定义的字段注解,每个字段与数据库中flyduck_user表相对应。注解中的value设置为数据库表中的列名。

public class AutoUser {
    @DbFieldProperty(value = "id", name = "id", clazz = String.class)
    private String id;

    @DbFieldProperty(value = "user_name", name = "用户名")
    private String userName;

    @DbFieldProperty(value = "password", name = "密码")
    private String password;

    @DbFieldProperty(value = "phone_number", name = "手机号")
    private String phoneNumber;

    @DbFieldProperty(value = "real_name", name = "姓名")
    private String realName;

    @DbFieldProperty(value = "email", name = "邮箱")
    private String email;

    @DbFieldProperty(value = "status", name = "状态", clazz = Integer.class)
    private Integer status;

    @DbFieldProperty(value = "is_delete", name = "删除")
    private Integer delete;

    @DbFieldProperty(value = "create_date", name = "创建时间", clazz = Date.class)
    private Date createDate;

    @DbFieldProperty(value = "update_date", name = "更新时间")
    private Date updateDate;

    // 省略 getter setter 代码

}
数据转换为对象

这里我们使用原始的JDBC方式从数据库表flyduck_user中获取数据,然后将通过执行SQL查询语句获得的数据集ResultSet转换为我们定义的AutoUser对象列表。

    @GetMapping("list")
    public String listUser() {
        String result = "";

        try {
            Class.forName(webConfig.getDbDriver());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        try (Connection c = DriverManager.getConnection(webConfig.getDbUrl(), webConfig.getDbUsername(), webConfig.getDbPassword());
             Statement s = c.createStatement();) {

            String sql = "select * from flyduck_user limit 10";

            Map> fieldMap = getFieldMap(AutoUser.class);


            // 执行查询语句,并把结果集返回给ResultSet
            ResultSet rs = s.executeQuery(sql);
            ResultSetmetaData resultSetmetaData = rs.getmetaData();
            List> list = new ArrayList<>();
            List userList = new ArrayList<>();
            while (rs.next()) {
                Map row = new HashMap<>();
                for (int index = 1; index <= resultSetmetaData.getColumnCount(); index++) {
                    row.put(resultSetmetaData.getColumnName(index).toLowerCase(), rs.getObject(index));
                }
                list.add(row);

                AutoUser user = buildObject(AutoUser.class, row, fieldMap);
                userList.add(user);
            }

            result = objectMapper.writevalueAsString(userList);

        } catch (Exception e) {
            // TODO 异常处理
        }

        return result;
    }

通过getFieldMap方法从类中将自定义字段注解的字段通过反射获取出来。

    private Map> getFieldMap(Class clazz) {
        List tempFieldList = new ArrayList();

        for(Class tempClass = clazz; tempClass != null; tempClass = tempClass.getSuperclass()) {
            Collections.addAll(tempFieldList, tempClass.getDeclaredFields());
        }

        Map> fieldMap = new HashMap<>();

        for (Field field : tempFieldList) {
            Map propertyMap = new HashMap<>();
            String fieldValue = field.getName().toLowerCase();

            propertyMap.put(KEY_PROPERTY, field.getName());

            DbFieldProperty fieldProperty = field.getAnnotation(DbFieldProperty.class);
            if (fieldProperty != null) {
                if (!StringUtils.isEmpty(fieldProperty.value())) {
                    fieldValue = fieldProperty.value().toLowerCase();
                }
                propertyMap.put(KEY_NAME, fieldProperty.name());
                propertyMap.put(KEY_TYPE, fieldProperty.clazz());
            }
            propertyMap.put(KEY_FIELD, fieldValue);

            fieldMap.put(fieldValue, propertyMap);
        }

        return fieldMap;
    }

通过buildObject方法,将从数据库表获取的数据按照自定义注解的方式转换初始化为对象。这个方法主要思路是按照类的字段注解提取数据放入map中,key为类的属性名。然后通过clazz.newInstance()创建对象,通过BeanMap.create(resultModel).putAll(map)给新创建的对象赋值。

    private  T buildObject(Class clazz, Map data, Map> fieldMap) throws IllegalAccessException, InstantiationException {
        Map map = new HashMap();
        for (Map.Entry entry : data.entrySet()) {
            Map propertyMap = fieldMap.get(entry.getKey());
            if (propertyMap != null) {
                Object value = entry.getValue();
                Class propertyClazz = (Class) propertyMap.get(KEY_TYPE);
                if (value != null && propertyClazz != null && !propertyClazz.equals(Object.class)) {
                    Object finalValue = propertyClazz.cast(value);
                    map.put(propertyMap.get(KEY_PROPERTY).toString(), finalValue);
                } else {
                    map.put(propertyMap.get(KEY_PROPERTY).toString(), value);
                }
            }
        }
        Object resultModel = clazz.newInstance();
        BeanMap.create(resultModel).putAll(map);
        return (T) resultModel;
    }
测试

我们这里的示例是在接口中,所以可以很方便的通过单元测试看下是否转换成功。

    @Test
    public void listUserTest() throws Exception {
        String result = mock
                .perform(get("/user/list")
                        .contentType(MediaType.APPLICATION_JSON_UTF8)
                )
                .andExpect(status().isOk())
                .andDo(print())
                .andReturn()
                .getResponse()
                .getContentAsString();

        assert !StringUtils.isEmpty(result) : "获取用户列表接口未通过测试";
    }

 单元测试通过,通过JDBC从数据库表中获取的数据成功转换并赋值给用户对象列表。

 总结

在实际项目中有很多类似的需求,如果是通过接口获取的json格式的数据,可以很容易的通过Json的包反序列化为需要的对象。但是类似于从Excel或者数据库表获取的数据,不是键值格式的数据,而是结构(列名)与数据分开的,我们可以通过自定义字段注解以及反射的方式转换为对象。本文仅仅从应用层面介绍了通过自定义字段注解和反射将数据转换为对象,未做进一步深入的研究,如果有描述错误的地方欢迎各位博友指正。

示例的源码已经上传到Gitee: https://gitee.com/flyduck128/springboot-demo/tree/master/flyduck-annotation

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/306095.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号