Java反射机制Reflection在项目中的应用
反射机制Reflection 简单的理解就是对class类的运用,在项目当中,适用于很多独特的场景,比如我们项目中的需求,1 我这边有两条数据,我主要知道两条数据哪些字段做了变更;2 我们跟别的webServer做联调,适配推送过去的字段等等。
1 几个注意核心类和方法
- Field : 提供有关类或接口的单个字段的信息和动态访问。
getName() :返回此Field对象表示的字段的名称.
getType(): 返回一个Class对象,该对象标识此Field对象表示的字段的声明类型 。- getDeclaredField(String name) : 返回一个Field对象。
- getDeclaredFields() :返回一个Field数组
- 当然用的的不止这些,具体看业务和Class 文档
2 生成公共类
public class Conversion {
//将model中与entity中类型和名称相同的属性值赋值给对应的entity的属性,并返回entity
public static <T1, T2> T2 TypeConversion(T1 model, T2 entity) {
List<Map<String, Object>> modelList = getFiledInfo(model);
List<Map<String, Object>> entityList = getFiledInfo(entity);
for (Map e : entityList) {
for (Map m : modelList) {
/**
* 判断类型和属性名是否都相同
*/
if (e.get("type").toString().equals(m.get("type").toString()) && e.get("name")
.toString()
.equals(m.get("name").toString())) {
try {
Field f = entity.getClass().getDeclaredField(e.get("name").toString());
f.setAccessible(true);
f.set(entity, m.get("value"));
} catch (Exception ex) {//查看其父类属性
try {
Field f = entity.getClass().getSuperclass().getDeclaredField(e.get("name").toString());
f.setAccessible(true);
f.set(entity, m.get("value"));
} catch (Exception e1) {
logger.error(ex.getMessage(), "conversion类型转换错误 " + ex);
}
}
}
}
}
return entity;
}
/**
* 根据属性名获取属性值
*/
private static Object getFieldValueByName(String fieldName, Object o) {
try {
String firstLetter = fieldName.substring(0, 1).toUpperCase();
String getter = "get" + firstLetter + fieldName.substring(1);
Method method = o.getClass().getMethod(getter, new Class[]{});
Object value = method.invoke(o, new Object[]{});
return value;
} catch (Exception e) {
logger.error(e.getMessage(), e);
return null;
}
}
/**
* 获取属性名数组
*/
private static String[] getFiledName(Object o) {
Field[] fields = o.getClass().getDeclaredFields();
String[] fieldNames = new String[fields.length];
for (int i = 0; i < fields.length; i++) {
fieldNames[i] = fields[i].getName();
}
return fieldNames;
}
/**
* 获取属性类型(type),属性名(name),属性值(value)的map组成的list
*/
private static List<Map<String, Object>> getFiledInfo(Object o) {
List<Map<String, Object>> list = new ArrayList<>();
List<Field> fields = new ArrayList<>();
fields.addAll(Arrays.asList(o.getClass().getDeclaredFields()));
/**
* 如果存在父类,获取父类的属性值,类型,名称并添加到一起
*/
Class sc = o.getClass().getSuperclass();
if (sc != null) {
fields.addAll(Arrays.asList(sc.getDeclaredFields()));
}
for (Field field : fields) {
Map<String, Object> infoMap = new HashMap<>();
infoMap.put("type", field.getType().toString());
infoMap.put("name", field.getName());
infoMap.put("value", getFieldValueByName(field.getName(), o));
list.add(infoMap);
}
return list;
}
}
3 项目实践:
- 3.1需求1 :我这边有两条数据,我想知道知道两条数据哪些字段做了变更【通过遍历,查出字段name相同的字段的value是否相同,记录下来,返回到List<Map<String, Object>> 集合中,接下来你就可以拿着这个结合做各种业务处理了】
// 这边比较数据的值 public List<Map<String, Object>> getCompareData(HecYbMedCodeSyncEntity entityHis, HecYbMedCodeSyncEntity entityNow) { List<Map<String, Object>> lists = new ArrayList<>(); List<Map<String, Object>> modelList = Conversion.getFiledInfo(entityHis); List<Map<String, Object>> entityList = Conversion.getFiledInfo(entityNow); modelList.forEach(mapHis -> { entityList.forEach(mapNow -> { Map<String, Object> map = new HashMap<>(); //这边需要把获取得到的字段名 跟 医保中心的字段名做个匹配,把我们这边的字段名转成他们那边的字段名 if ( mapHis.get("name").toString().equals(mapNow.get("name").toString())) {//判断名字相同 if (!(mapHis.get("value").toString().equals(mapNow.get("value").toString())) ) {//判断值不相同 且 反射实体不为空 map.put("bgzdbm", fieldMappedEnum.getSourceFieldName()); //变更字段编码 map.put("bgzdmc", fieldMappedEnum.getDesc()); //变更字段名称 map.put("bgqz00", mapHis.get("value").toString()); //变更前值 map.put("bghz00", mapNow.get("value").toString()); //变更后值 map.put("bgsj00", DateUtil.limitDay(entityNow.getSyncTime())); //变更时间 lists.add(map); } } }); });
- 3.2 我们跟别的webServer做联调,我需要把我的数据库字段的名称改成webServer上的值。
我的设计思路是,设计一个枚举,把我数据库的字段A,和webServer接口上的字段对应上B,这样给B赋值的时候就可以通过枚举获取得到A,并且通过A就可以获取到A的值,这样就绕过了人工的给B赋值的繁琐操作。
调用如下:
FieldMappedEnum fieldMappedEnum = FieldMappedEnum.findSourceFieldByTarget(mapHis.get("name").toString());
map.put("bgzdbm", fieldMappedEnum.getSourceFieldName()); //变更字段编码
map.put("bgzdmc", fieldMappedEnum.getDesc()); //变更字段名称
....