SpringBoot通过⾃定义字段注解以及反射获取对象
在Java的开发过程中,注解的应⽤场景是⾮常⼴泛的。Java也提供了很多内置的注解,⽐如
@Override,@Deprecated,@SuppressWarnings等等。之前也写过⼀篇注解相关的⽂章,。本⽂主要介绍通过⾃定义字段注解以及反射,实现初始化对象的功能。应⽤场景主要是通过外部接⼝,数据库,⽂本或者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.DbDriver());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try (Connection c = DbUrl(), DbUsername(), DbPassword());
Statement s = c.createStatement();) {
String sql = "select * from flyduck_user limit 10";
Map<String, Map<String, Object>> fieldMap = getFieldMap(AutoUser.class);
// 执⾏查询语句,并把结果集返回给ResultSet
ResultSet rs = s.executeQuery(sql);
ResultSetMetaData resultSetMetaData = rs.getMetaData();
List<Map<String, Object>> list = new ArrayList<>();
List<AutoUser> userList = new ArrayList<>();
while (rs.next()) {
Map<String, Object> row = new HashMap<>();
for (int index = 1; index <= ColumnCount(); index++) {
row.ColumnName(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<String, Map<String, Object>> getFieldMap(Class clazz) {
List<Field> tempFieldList = new ArrayList();
for(Class tempClass = clazz; tempClass != null; tempClass = Superclass()) {
Collections.addAll(tempFieldList, DeclaredFields());
}
Map<String, Map<String, Object>> fieldMap = new HashMap<>();
for (Field field : tempFieldList) {
Map<String, Object> propertyMap = new HashMap<>();
String fieldValue = Name().toLowerCase();
propertyMap.put(KEY_PROPERTY, Name());
DbFieldProperty fieldProperty = Annotation(DbFieldProperty.class);
if (fieldProperty != null) {
if (!StringUtils.isEmpty(fieldProperty.value())) {
fieldValue = fieldProperty.value().toLowerCase();
}
propertyMap.put(KEY_NAME, fieldProperty.name());
springboot结构propertyMap.put(KEY_TYPE, fieldProperty.clazz());
}
propertyMap.put(KEY_FIELD, fieldValue);
fieldMap.put(fieldValue, propertyMap);
}
return fieldMap;
}
通过buildObject⽅法,将从数据库表获取的数据按照⾃定义注解的⽅式转换初始化为对象。这个⽅法主要思路是按照类的字段注解提取数
据放⼊map中,key为类的属性名。然后通过wInstance()创建对象,通过ate(resultModel).putAll(map)给新创建的对象赋值。
private <T> T buildObject(Class clazz, Map<String, Object> data, Map<String, Map<String, Object>> fieldMap) throws IllegalAccessException, Instantiat Map<String, Object> map = new HashMap();
for (Map.Entry<String, Object> entry : Set()) {
Map<String, Object> propertyMap = (Key());
if (propertyMap != null) {
Object value = Value();
Class propertyClazz = (Class) (KEY_TYPE);
if (value != null && propertyClazz != null && !propertyClazz.equals(Object.class)) {
Object finalValue = propertyClazz.cast(value);
map.(KEY_PROPERTY).toString(), finalValue);
} else {
map.(KEY_PROPERTY).toString(), value);
}
}
}
Object resultModel = wInstance();
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或者数据库表获取的数据,不是键值格式的数据,⽽是结构(列名)与数据分开的,我们可以通过⾃定义字段注解以及反射的⽅式转换为对象。本⽂仅仅从应⽤层⾯介绍了通过⾃定义字段注解和反射将数据转换为对象,未做进⼀步深⼊的研究,如果有描述错误的地⽅欢迎各位博友指正。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论