導讀
上一篇文章已經詳細介紹了框架與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));
測試結果爲:
可見,我們算法時成功的。
總結
我們還是要時常看源碼,因爲,你的目的不是寫出框架,而是看別人寫框架的思想。畢竟,思想主導一切行爲,行爲成就一個的未來。致努力的自己。