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

為什么要避免用Apache Beanutils進(jìn)行屬性的copy

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

大家好,我是哪吒。

今天,通過代碼實(shí)例、源碼解讀、四大工具類橫向?qū)Ρ鹊姆绞?,和大家一起聊一聊?duì)象賦值的問題。

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

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

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

問:下圖這樣?

圖片

答:對(duì)啊,你怎么能把我的代碼放到網(wǎng)上?

問:沒,我只是舉個(gè)例子

答:這涉及到商業(yè)機(jī)密,是很嚴(yán)重的問題

問:我發(fā)現(xiàn)你挺能扯皮啊,直接回答問題行嗎?

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

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

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

答:很簡(jiǎn)單啊,可以通過工具類Beanutils直接賦值啊

問:我聽說工具類最近很卷,你用的哪個(gè)?。?/h4>

答:就Apache自帶的那個(gè)啊,賊簡(jiǎn)單。我手寫一個(gè),給你欣賞一下。

圖片

問:你這代碼報(bào)錯(cuò)啊,避免用Apache Beanutils進(jìn)行屬性的copy。

答:沒報(bào)錯(cuò),只是嚴(yán)重警告而已,代碼能跑就行,有問題再優(yōu)化唄

問:你這什么態(tài)度?人事在哪劃拉的人,為啥會(huì)出現(xiàn)嚴(yán)重警告?

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

問:具體什么原因?qū)е碌哪兀?/p>

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

通過單例模式調(diào)用copyProperties,但是,每一個(gè)方法對(duì)應(yīng)一個(gè)BeanUtilsBean.getInstance()實(shí)例,每一個(gè)類實(shí)例對(duì)應(yīng)一個(gè)實(shí)例,這不算一個(gè)真正的單例模式。

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

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

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

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 提供了可以動(dòng)態(tài)修改實(shí)現(xiàn)他的類的屬性名稱、屬性值、屬性類型的功能
if (orig instanceof DynaBean) {
// 獲取源對(duì)象所有屬性
DynaProperty[] origDescriptors = ((DynaBean)orig).getDynaClass().getDynaProperties();
DynaProperty[] var4 = origDescriptors;
var5 = origDescriptors.length;

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

}
}

通過 jvisualvm.exe 檢測(cè)代碼性能

再通過jvisualvm.exe檢測(cè)一下運(yùn)行情況,果然,logging.log4j赫然在列,穩(wěn)居耗時(shí)Top1。

圖片

問:還有其它好的方式嗎?性能好一點(diǎn)的

答:當(dāng)然有,據(jù)我了解有 4 種工具類,實(shí)際上,可能會(huì)有更多,話不多說,先簡(jiǎn)單介紹一下。

  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);
}
}
}

"四大金剛" 性能統(tǒng)計(jì)

方法

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毫秒

不測(cè)不知道,一測(cè)嚇一跳,差的還真的多。

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

性能走勢(shì) --> spring cglib BeanCopier 優(yōu)于 spring copyProperties 優(yōu)于 apache PropertyUtils 優(yōu)于 apache BeanUtils

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

Apache PropertyUtils 源碼分析?

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

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

DanyBean 提供了可以動(dòng)態(tài)修改實(shí)現(xiàn)他的類的屬性名稱、屬性值、屬性類型的功能。

public void copyProperties(Object dest, Object orig){
// 判斷數(shù)據(jù)源和目標(biāo)對(duì)象不是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中最為耗時(shí)的log日志記錄
int var5;
int var6;
String name;
Object value;
// 類型檢查
if (orig instanceof DynaBean) {
// 獲取源對(duì)象所有屬性
DynaProperty[] origDescriptors = ((DynaBean)orig).getDynaClass().getDynaProperties();
DynaProperty[] var4 = origDescriptors;
var5 = origDescriptors.length;

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

}
}

通過 jvisualvm.exe 檢測(cè)代碼性能

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

Spring copyProperties 源碼分析?

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

// 判斷數(shù)據(jù)源和目標(biāo)對(duì)象不是null
Assert.notNull(source, "Source must not be null");
Assert.notNull(target, "Target must not be null");

/**
* 若target設(shè)置了泛型,則默認(rèn)使用泛型
* 若是 editable 是 null,則此處忽略
* 一般情況下editable都默認(rèn)為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();
// 目標(biāo)對(duì)象存在寫入方法、屬性不被忽略
if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
if (sourcePd != null) {
Method readMethod = sourcePd.getReadMethod();
/**
* 源對(duì)象存在讀取方法、數(shù)據(jù)是可復(fù)制的
* writeMethod.getParameterTypes()[0]:獲取 writeMethod 的第一個(gè)入?yún)㈩愋?/span>
* readMethod.getReturnType():獲取 readMethod 的返回值類型
* 判斷返回值類型和入?yún)㈩愋褪欠翊嬖诶^承關(guān)系,只有是繼承關(guān)系或相等的情況下,才會(huì)進(jìn)行注入
*/
if (readMethod != null &&
ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
// 放開讀取方法的權(quán)限
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
// 通過反射獲取值
Object value = readMethod.invoke(source);
// 放開寫入方法的權(quán)限
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
// 通過反射寫入值
writeMethod.invoke(target, value);
}
}
}
}
}

總結(jié)?

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

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

Apache PropertyUtils 進(jìn)行了如下優(yōu)化:

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

Spring copyProperties 進(jìn)行了如下優(yōu)化:

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

本文轉(zhuǎn)載自微信公眾號(hào)「哪吒編程」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系哪吒編程公眾號(hào)。

責(zé)任編輯:姜華 來源: 哪吒編程
相關(guān)推薦

2020-09-08 16:25:18

Apache BeancopyJava

2020-07-30 12:16:33

阿里巴巴Apache對(duì)象

2022-12-26 00:25:06

2022-05-10 15:24:34

KafkaZooKeeperKafka Raft

2023-09-21 16:17:48

數(shù)據(jù)驅(qū)動(dòng)運(yùn)營(yíng)

2022-01-03 08:06:15

函數(shù)Go數(shù)據(jù)

2024-06-04 00:10:00

開發(fā)拷貝

2024-02-17 21:57:10

2013-03-12 14:30:09

Ubuntu操作系統(tǒng)

2015-08-06 10:14:15

造輪子facebook

2022-08-15 08:27:02

基站網(wǎng)絡(luò)

2017-08-02 16:47:43

數(shù)據(jù)數(shù)據(jù)收集數(shù)據(jù)分析

2014-05-19 15:52:57

Apache StraApache

2018-05-23 00:20:29

2019-09-30 07:50:51

ITOps云端ITOM

2019-12-26 14:52:31

微軟CortanaAndroid

2016-08-19 16:27:52

數(shù)據(jù)庫Mongo DB開發(fā)

2017-04-05 18:10:05

R語言開發(fā)Ross

2021-03-16 08:35:14

Kubernetes Docker容器

2022-03-30 08:21:57

合并HTTP
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 日韩欧美在线播放 | 天天干夜夜操 | 日韩一区二区精品 | 国产一区二区中文字幕 | 欧美在线一区二区三区 | 久久99久久98精品免观看软件 | 日本一区二区不卡 | 精品视频在线免费观看 | 在线日韩中文字幕 | 国产精品视频二区三区 | 久久人人爽人人爽人人片av免费 | 青青久久av北条麻妃海外网 | 操人网站| 青青草在线播放 | 欧美综合在线观看 | 亚洲日韩欧美一区二区在线 | 青青草精品 | 成人深夜小视频 | 国产精品精品久久久 | 午夜在线观看视频 | 99精品国自产在线观看 | 日本精品视频 | 欧美vide | 91亚洲精品久久久电影 | 99这里只有精品视频 | 免费精品| 欧美精品久久久 | 国产精品视频一区二区三区 | 精区3d动漫一品二品精区 | 男女视频在线观看 | 国产大片一区 | 国产成人免费视频网站高清观看视频 | 亚洲精品日韩视频 | 九色视频网站 | 九九热国产精品视频 | 中文字幕1区2区3区 日韩在线视频免费观看 | 亚洲精品视频三区 | 啪啪av| 日本精品一区二区三区在线观看视频 | 在线91| 国产农村一级国产农村 |