成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

為什么要避免用Apache Beanutils進行屬性的copy

開發 前端
在實際的項目開發中,對象間賦值普遍存在,隨著雙十一、秒殺等電商過程愈加復雜,數據量也在不斷攀升,效率問題,浮出水面。

大家好,我是哪吒。

今天,通過代碼實例、源碼解讀、四大工具類橫向對比的方式,和大家一起聊一聊對象賦值的問題。

在實際的項目開發中,對象間賦值普遍存在,隨著雙十一、秒殺等電商過程愈加復雜,數據量也在不斷攀升,效率問題,浮出水面。

問:如果是你來寫對象間賦值的代碼,你會怎么做?

答:想都不用想,直接代碼走起來,get、set即可。

問:下圖這樣?

圖片

答:對啊,你怎么能把我的代碼放到網上?

問:沒,我只是舉個例子

答:這涉及到商業機密,是很嚴重的問題

問:我發現你挺能扯皮啊,直接回答問題行嗎?

答:OK,OK,我也覺得這樣寫很low,上次這么寫之后,差點挨打

  1. 對象太多,ctrl c + strl v,鍵盤差點沒敲壞;
  2. 而且很容易出錯,一不留神,屬性沒對應上,賦錯值了;
  3. 代碼看起來很傻缺,一個類好幾千行,全是get、set復制,還起個了自以為很優雅的名字transfer;
  4. 如果屬性名不能見名知意,還得加上每個屬性的含義注釋(基本這種賦值操作,都是要加的,注釋很重要,注釋很重要,注釋很重要);
  5. 代碼維護起來很麻煩;
  6. 如果對象過多,會產生類爆炸問題,如果屬性過多,會嚴重違背阿里巴巴代碼規約(一個方法的實際代碼最多20行);

問:行了,行了,說說,怎么解決吧。

答:很簡單啊,可以通過工具類Beanutils直接賦值啊

問:我聽說工具類最近很卷,你用的哪個啊?

答:就Apache自帶的那個啊,賊簡單。我手寫一個,給你欣賞一下。

圖片

問:你這代碼報錯啊,避免用Apache Beanutils進行屬性的copy。

答:沒報錯,只是嚴重警告而已,代碼能跑就行,有問題再優化唄

問:你這什么態度?人事在哪劃拉的人,為啥會出現嚴重警告?

答:拿多少錢,干多少活,我又不是XXX,應該是性能問題吧

問:具體什么原因導致的呢?

答:3000塊錢還得手撕一下 apache copyProperties 的源代碼唄?

通過單例模式調用copyProperties,但是,每一個方法對應一個BeanUtilsBean.getInstance()實例,每一個類實例對應一個實例,這不算一個真正的單例模式。

public static void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException {
BeanUtilsBean.getInstance().copyProperties(dest, orig);
}

性能瓶頸 --> 日志太多也是病

通過源碼可以看到,每一個copyProperties都要進行多次類型檢查,還要打印日志。

public void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException {
// 類型檢查
if (dest == null) {
throw new IllegalArgumentException("No destination bean specified");
} else if (orig == null) {
throw new IllegalArgumentException("No origin bean specified");
} else {
// 打印日志
if (this.log.isDebugEnabled()) {
this.log.debug("BeanUtils.copyProperties(" + dest + ", " + orig + ")");
}

int var5;
int var6;
String name;
Object value;
// 類型檢查
// DanyBean 提供了可以動態修改實現他的類的屬性名稱、屬性值、屬性類型的功能
if (orig instanceof DynaBean) {
// 獲取源對象所有屬性
DynaProperty[] origDescriptors = ((DynaBean)orig).getDynaClass().getDynaProperties();
DynaProperty[] var4 = origDescriptors;
var5 = origDescriptors.length;

for(var6 = 0; var6 < var5; ++var6) {
DynaProperty origDescriptor = var4[var6];
// 獲取源對象屬性名
name = origDescriptor.getName();
// 判斷源對象是否可讀、判斷目標對象是否可寫
if (this.getPropertyUtils().isReadable(orig, name) && this.getPropertyUtils().isWriteable(dest, name)) {
// 獲取對應的值
value = ((DynaBean)orig).get(name);
// 每個屬性都調用一次copyProperty
this.copyProperty(dest, name, value);
}
}
} else if (orig instanceof Map) {
...
} else {
...
}

}
}

通過 jvisualvm.exe 檢測代碼性能

再通過jvisualvm.exe檢測一下運行情況,果然,logging.log4j赫然在列,穩居耗時Top1。

圖片

問:還有其它好的方式嗎?性能好一點的

答:當然有,據我了解有 4 種工具類,實際上,可能會有更多,話不多說,先簡單介紹一下。

  1. org.apache.commons.beanutils.BeanUtils。
  2. org.apache.commons.beanutils.PropertyUtils。
  3. org.springframework.cglib.beans.BeanCopier。
  4. org.springframework.beans.BeanUtils。

問:那你怎么不用?

答:OK,我來演示一下

public class Test {

private static void apacheBeanUtilsCopyTest(User source, User target, int sum){
for (int i = 0; i < sum; i++) {
org.apache.commons.beanutils.BeanUtils.copyProperties(source, target);
}
}

private static void commonsPropertyCopyTest(User source, User target, int sum){
for (int i = 0; i < sum; i++) {
org.apache.commons.beanutils.PropertyUtils.copyProperties(target, source);
}
}

static BeanCopier copier = BeanCopier.create(User.class, User.class, false);
private static void cglibBeanCopyTest(User source, User target, int sum){
for (int i = 0; i < sum; i++) {
org.springframework.cglib.beans.BeanCopier.copier.copy(source, target, null);
}
}

private static void springBeanCopy(User source, User target, int sum){
for (int i = 0; i < sum; i++) {
org.springframework.beans.BeanUtils.copyProperties(source, target);
}
}
}

"四大金剛" 性能統計

方法

1000

10000

100000

1000000

apache BeanUtils

906毫秒

807毫秒

1892毫秒

11049毫秒

apache PropertyUtils

17毫秒

96毫秒

648毫秒

5896毫秒

spring cglib BeanCopier

0毫秒

1毫秒

3毫秒

10毫秒

spring copyProperties

87毫秒

90毫秒

123毫秒

482毫秒

不測不知道,一測嚇一跳,差的還真的多。

spring cglib BeanCopier性能最好,apache BeanUtils性能最差。

性能走勢 --> spring cglib BeanCopier 優于 spring copyProperties 優于 apache PropertyUtils 優于 apache BeanUtils

避免用Apache Beanutils進行屬性的copy的問題 上面分析完了,下面再看看其它的方法做了哪些優化。

Apache PropertyUtils 源碼分析?

從源碼可以清晰的看到,類型檢查變成了非空校驗,去掉了每一次copy的日志記錄,性能肯定更好了。

  1. 類型檢查變成了非空校驗
  2. 去掉了每一次copy的日志記錄
  3. 實際賦值的地方由copyProperty變成了DanyBean  + setSimpleProperty;

DanyBean 提供了可以動態修改實現他的類的屬性名稱、屬性值、屬性類型的功能。

public void copyProperties(Object dest, Object orig){
// 判斷數據源和目標對象不是null
if (dest == null) {
throw new IllegalArgumentException("No destination bean specified");
} else if (orig == null) {
throw new IllegalArgumentException("No origin bean specified");
} else {
// 刪除了org.apache.commons.beanutils.BeanUtils.copyProperties中最為耗時的log日志記錄
int var5;
int var6;
String name;
Object value;
// 類型檢查
if (orig instanceof DynaBean) {
// 獲取源對象所有屬性
DynaProperty[] origDescriptors = ((DynaBean)orig).getDynaClass().getDynaProperties();
DynaProperty[] var4 = origDescriptors;
var5 = origDescriptors.length;

for(var6 = 0; var6 < var5; ++var6) {
DynaProperty origDescriptor = var4[var6];
// 獲取源對象屬性名
name = origDescriptor.getName();
// 判斷源對象是否可讀、判斷目標對象是否可寫
if (this.isReadable(orig, name) && this.isWriteable(dest, name)) {
// 獲取對應的值
value = ((DynaBean)orig).get(name);
// 相對于org.apache.commons.beanutils.BeanUtils.copyProperties此處有優化
// DanyBean 提供了可以動態修改實現他的類的屬性名稱、屬性值、屬性類型的功能
if (dest instanceof DynaBean) {
((DynaBean)dest).set(name, value);
} else {
// 每個屬性都調用一次copyProperty
this.setSimpleProperty(dest, name, value);
}
}
}
} else if (orig instanceof Map) {
...
} else {
...
}

}
}

通過 jvisualvm.exe 檢測代碼性能

再通過jvisualvm.exe檢測一下運行情況,果然,logging.log4j沒有了,其他的基本不變。

Spring copyProperties 源碼分析?

  1. 判斷數據源和目標對象的非空判斷改為了斷言。
  2. 每次copy沒有日志記錄。
  3. 沒有if (orig instanceof DynaBean) {這個類型檢查。
  4. 增加了放開權限的步驟。
private static void copyProperties(Object source, Object target, @Nullable Class<?> editable,
@Nullable String... ignoreProperties){

// 判斷數據源和目標對象不是null
Assert.notNull(source, "Source must not be null");
Assert.notNull(target, "Target must not be null");

/**
* 若target設置了泛型,則默認使用泛型
* 若是 editable 是 null,則此處忽略
* 一般情況下editable都默認為null
*/
Class<?> actualEditable = target.getClass();
if (editable != null) {
if (!editable.isInstance(target)) {
throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
"] not assignable to Editable class [" + editable.getName() + "]");
}
actualEditable = editable;
}

// 獲取target中全部的屬性描述
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
// 需要忽略的屬性
List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);

for (PropertyDescriptor targetPd : targetPds) {
Method writeMethod = targetPd.getWriteMethod();
// 目標對象存在寫入方法、屬性不被忽略
if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
if (sourcePd != null) {
Method readMethod = sourcePd.getReadMethod();
/**
* 源對象存在讀取方法、數據是可復制的
* writeMethod.getParameterTypes()[0]:獲取 writeMethod 的第一個入參類型
* readMethod.getReturnType():獲取 readMethod 的返回值類型
* 判斷返回值類型和入參類型是否存在繼承關系,只有是繼承關系或相等的情況下,才會進行注入
*/
if (readMethod != null &&
ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
// 放開讀取方法的權限
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
// 通過反射獲取值
Object value = readMethod.invoke(source);
// 放開寫入方法的權限
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
// 通過反射寫入值
writeMethod.invoke(target, value);
}
}
}
}
}

總結?

阿里的友情提示,避免用Apache Beanutils進行對象的copy,還是很有道理的。

Apache Beanutils 的性能問題出現在類型校驗和每一次copy的日志記錄。

Apache PropertyUtils 進行了如下優化:

  1. 類型檢查變成了非空校驗。
  2. 去掉了每一次copy的日志記錄。
  3. 實際賦值的地方由copyProperty變成了DanyBean  + setSimpleProperty。

Spring copyProperties 進行了如下優化:

  1. 判斷數據源和目標對象的非空判斷改為了斷言。
  2. 每次copy沒有日志記錄。
  3. 沒有if (orig instanceof DynaBean) {這個類型檢查。
  4. 增加了放開權限的步驟。

本文轉載自微信公眾號「哪吒編程」,可以通過以下二維碼關注。轉載本文請聯系哪吒編程公眾號。

責任編輯:姜華 來源: 哪吒編程
相關推薦

2020-09-08 16:25:18

Apache BeancopyJava

2020-07-30 12:16:33

阿里巴巴Apache對象

2022-12-26 00:25:06

2022-05-10 15:24:34

KafkaZooKeeperKafka Raft

2023-09-21 16:17:48

數據驅動運營

2022-01-03 08:06:15

函數Go數據

2024-06-04 00:10:00

開發拷貝

2024-02-17 21:57:10

2015-08-06 10:14:15

造輪子facebook

2022-08-15 08:27:02

基站網絡

2013-03-12 14:30:09

Ubuntu操作系統

2014-05-19 15:52:57

Apache StraApache

2017-08-02 16:47:43

數據數據收集數據分析

2014-08-25 10:00:18

開源

2015-05-12 11:04:42

Java EE學習Java EE

2019-11-27 10:25:15

SaaS云端IT架構

2017-04-05 16:40:45

2017-09-08 08:35:16

Android代碼API設計

2021-02-11 13:30:56

Nodejs源碼c++

2019-03-19 08:59:13

物聯網IOT技術
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 一区二区手机在线 | 美女爽到呻吟久久久久 | 一级美国黄色片 | 国产精品视频在线免费观看 | 麻豆一区二区三区 | 一区不卡在线观看 | 欧美福利| 亚洲a视频| 亚洲国产电影 | 精品中文在线 | 成人黄色网址大全 | 特黄毛片 | 一级全黄少妇性色生活免费看 | 久久精品在线 | 一区二区在线观看av | 久久r精品 | 一区二区三区四区免费观看 | 91最新入口| 蜜桃臀av一区二区三区 | 欧美久久综合 | 久久精品高清视频 | 女人av | 亚洲综合色丁香婷婷六月图片 | 精品国产视频在线观看 | 超碰成人免费观看 | 黄色毛片免费看 | 波多野结衣一区二区 | 久久久精品一区二区 | 成人网址在线观看 | 中文字幕国产 | 超碰免费在线 | 欧美精品一区在线观看 | 成人免费视频网站在线看 | 久久一区二区av | 天天操天天干天天爽 | 色综合久久久 | 国产精品国产精品国产专区不卡 | 麻豆精品久久 | 成人小视频在线免费观看 | 黄色欧美 | 中文字幕国产一区 |