乐趣区

只因数据过滤,方可模拟beanutils框架

导读
上一篇文章已经详细介绍了框架与 RTTI 的关系,RTTI 与反射之间的关系。其中详细介绍了框架与反射的关系,这也是很多培训机构把反射作为高级教程来讲解。
其实,我工作年限也不长,大概八九个月吧。但我见过很多技术人员,而我喜欢与别人讨论技术。从中也知道了,很多公司没有实现数据过滤。
什么是数据过滤?比如客户端向服务器端发送展示项目图片的请求,服务端接收到前端的请求并从数据库中拿到项目图片的对象,我们只要返回图片的在服务端的地址和名称即可,没必要将整个图片对象返回给客户端,因为,那样将会造成数据的冗余。因而,我们这时需要过滤数据(对象),如代码所示:
/**
* 常用的把图片转成 {id: 1, path: “xxx”} 结构
*/
public static JSONObject img2Json(Picture picture) {
if (isNotNull(picture)) {
String[] PICTURE_JSON = {“id”, “remoteRelativeUrl:path”};
JSONObject jsonObject = propsFilter(picture, PICTURE_JSON);
return jsonObject;
} else {
return null;
}
}
如上诉代码的转换,公司使用的是 commons-beanutils 这个框架。我们只要在项目中农添加其 maven 配置即可:
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.2</version>
</dependency>
我个人比较喜欢研究源码,于是,仿照这个框架写了自己的框架,下面,就是介绍我个人的框架。
我的 beanutils 框架

框架使用的算法或技术

递归算法。我们并不推荐使用递归,因为,方法自调用自己。根据 JVM 的内部原理,每个方法都是一个方法栈。而栈是存放数据的一种结构,其采用 FIFO(First In Last Out),即先进后出。和我们堆放菜盘一样,先垒的最后拿出来。既然是数据存储,肯定会超出容量,因为,内存不是无限大的,恰如水满自溢。但是,我们在这里还是使用递归,因为,在深度调用算法当中,采用递归是合适的。
java 的反射机制。我们根据 Javabean 的属性名称获取值。

核心算法说明。

如果 javabean 的对象属性类型不是用户自定义的类型,我们根据反射调用 get 方法拿到属性的值如果 javabean 的对象属性类型是用户自定义的类型,我们利用递归重新调用改方法,直到出现遇见上面的条件
/**
* Created By zby on 20:28 2019/2/13
*
* @param bean 实体对象
* @param props 属性名称
*/
public static Object getProperty(Object bean, String props) {
if (bean == null)
throw new RuntimeException(“ 实例化对象不存在 bean=” + bean);
if (StringHelper.isBlank(props))
throw new RuntimeException(“ 属性名称不存在 props=” + props);
Class<?> clazz = null;
String methodName = null;
String fieldName = null;
String typeName = null;
try {
clazz = bean.getClass();
if (props.indexOf(“.”) != -1) {
methodName = MethodHelper.propsToGetMethod(props.substring(0, props.indexOf(“.”)));
Method method = clazz.getDeclaredMethod(methodName);
Object obj = method.invoke(bean);
return getProperty(obj, props.substring(props.indexOf(“.”) + 1));
}
Field field = clazz.getDeclaredField(props);
typeName = field.getType().getName();
if (typeName.equalsIgnoreCase(“boolean”)) {
field.setAccessible(true);
return field.getBoolean(bean);
}

methodName = MethodHelper.propsToGetMethod(props);
Method method = clazz.getDeclaredMethod(methodName);
return method.invoke(bean);
} catch (NoSuchMethodException e) {
logger.error(clazz + “ 类型没有 ” + methodName + “ 方法 ”);
e.printStackTrace();
} catch (NoSuchFieldException e) {
logger.error(clazz + “ 类型没有 ” + fieldName + “ 属性 ”);
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}

如何使用上诉算法
我们既然是通过属性名称来获取属性对象。我们可以设计一个算法,算法该算法有两个参数,一个是当前对象,一个是对象的属性数组。属性数组还可以有别名。
为什么需要别名?比如当前对象采用组合关系,使用自定义的类。比如说订单类使用用户类(User)的对象作为属性,我们在订单中希望看到用户姓名,我们可以这样调用 user.name,以该字段传给客户端,但客户端需要转换才能拿到用户名,因而,我们需要一个别名,前端不用转换,就可以拿到用户名,比如:user.name:username。
当然,我们需要将对象转化为 json 格式的框架,这里使用的阿里巴巴的 fastjson 框架,我们可以在项目中配置 maven:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.28</version>
</dependency>
所示,算法设计为:
/**
* Created By zby on 9:40 2019/2/13
* 模拟框架中的数据
*
* @param object 参数对象
* @param props String 类型的变长数组,比如 {“id:projectId”, “mobile”,…}
* 前半部分是 javabean 的属性名,后半部分是返回到给客户端的参数名
*/
public static JSONObject propsFilter(Object object, String… props) {
//【1】判断对象和变长数组的是否为空,以及变长数组的长度是否为 0
boolean isNull = object == null || (null == props && props.length == 0);
if (isNull) {
logger.warn(“ 参数为空 object=” + object + “props=” + props);
return null;
}
JSONObject jsonObject = new JSONObject();
for (String prop : props) {
//【2】再判断对象不为空,或者长度不为 0
if (prop == null && prop.length() == 0) {
logger.warn(“ 参数为空 prop= ” + prop);
throw new RuntimeException(“ 参数为空 prop=” + prop);
}
Object o = null;
String[] namePair = StringUtils.split(prop, “:”);
try {
o = PropertyUtil.getProperty(object, namePair[0]);
} catch (Exception e) {
logger.warn(“ 类 ” + object.getClass() + “,属性 ” + namePair[0] + “ 不存在 ”);
}
String key = namePair.length <= 1 ? namePair[0] : namePair[1];
if (o instanceof Date)
jsonObject.put(key, DateUtil.simpleFormate((Date) o));
else if (o instanceof BigDecimal)
jsonObject.put(key, CommonUtil.toFiexd((BigDecimal) o, 2));
else if (o instanceof TitleEnum)
jsonObject.put(key, CommonUtil.builderEnum((TitleEnum) o));
else
jsonObject.put(key, o);
}
return jsonObject;
}

测试框架和类
我们既然写好了这个框,也使用了这个框架,因而,我们可以使用 Junit 来测试:
@Test
public void test(){
Address address = new Address();
address.setAddressTag(AddressTagEnum.ADDRESS_TAG_COMPANY);
address.setArea(“ 杭州市 ….”);
address.setConsignee(“zby”);

User user = new User();
user.setHobby(HobbyEnum.HOBBY_DANCING);
user.setGender(“ 男 ”);
user.setUserName(“ 蒋三 ”);

OrderSnapshot orderSnapshot = new OrderSnapshot();
orderSnapshot.setAddress(address);
orderSnapshot.setId(1L);
orderSnapshot.setName(“ 复读机 ”);
orderSnapshot.setOrderNo(Long.valueOf(System.currentTimeMillis()).toString() + “1L”);
orderSnapshot.setUser(user);
String[] json = {“address.consignee:consignee”,”user.hobby:hobby”,
“address.addressTag:addressTag”, “address.area:area”
,”address.consignee:consignee”,”user.userName:userName”};
System.out.println(JsonUtil.propsFilter(orderSnapshot, json));
测试结果为:

可见,我们算法时成功的。
总结
我们还是要时常看源码,因为,你的目的不是写出框架,而是看别人写框架的思想。毕竟,思想主导一切行为,行为成就一个的未来。致努力的自己。

退出移动版