Warum wird die Verwendung von BeanUtils.copyProperties zum Kopieren von Daten nicht empfohlen?

In der tatsächlichen Geschäftsentwicklung stoßen wir häufig auf Zuweisungen zwischen Objektattributen wie VO, BO, PO, DTO usw. Wenn viele Attribute vorhanden sind, ist der Arbeitsaufwand für die Zuweisung von Werten mithilfe der Get- und Set-Methoden relativ groß, sodass die Arbeitsbelastung relativ groß ist relativ groß. Viele Leute werden sich dafür entscheiden, die copyProperties-Methode von BeanUtils zu verwenden, ein von Spring bereitgestelltes Kopiertool, um Eigenschaften zwischen Objekten zu kopieren. Diese Methode kann unseren Arbeitsaufwand beim manuellen Schreiben von Objektattributzuweisungscode erheblich reduzieren. Warum wird sie nicht empfohlen, da sie so praktisch ist? Im Folgenden sind einige häufige Fallstricke beim Kopieren von BeanUtils.copyProperties-Daten aufgeführt, die ich kompiliert habe .

1: Inkonsistente Attributtypen verursachen einen Kopierfehler

Diese Grube kann in die folgenden zwei Typen unterteilt werden:
(1) Das gleiche Attribut hat verschiedene Typen
In der tatsächlichen Entwicklung ist es sehr wahrscheinlich, dass für dasselbe Feld in verschiedenen Klassen inkonsistente Typen definiert sind. Beispielsweise kann die ID in Klasse A als Long und in Klasse B als String definiert sein. In diesem Fall, wenn Sie BeanUtils beim Kopieren verwenden. copyProperties schlägt das Kopieren fehl und das entsprechende Feld ist null. Die entsprechenden Fälle sind wie folgt:
  
  
  
  
  
public class BeanUtilsTest {
public static void main(String[] args) { SourcePoJo sourcePoJo = new SourcePoJo("jingdong", (long) 35711); TargetPoJo targetPoJo = new TargetPoJo(); BeanUtils.copyProperties(sourcePoJo,targetPoJo); System.out.println(targetPoJo); }}@Data@AllArgsConstructorclass SourcePoJo{ private String username; private Long id;}
@Dataclass TargetPoJo{ private String username; private String id;}
Die entsprechenden Laufergebnisse lauten wie folgt:
Sie können sehen, dass der kopierte Wert des ID-Felds aufgrund inkonsistenter Typen null ist.
(2) Das gleiche Feld verwendet den Verpackungstyp bzw. den Basistyp
Wenn Sie für ein Feld eine Wrapper-Klasse bzw. einen Basistyp verwenden, tritt eine Ausnahme auf, wenn kein tatsächlicher Wert übergeben wird. Die konkreten Fälle sind wie folgt:
  
  
  
  
  
public class BeanUtilsTest {
public static void main(String[] args) { SourcePoJo sourcePoJo = new SourcePoJo(); sourcePoJo.setUsername("joy"); TargetPoJo targetPoJo = new TargetPoJo(); BeanUtils.copyProperties(sourcePoJo,targetPoJo); System.out.println(targetPoJo); }}@Dataclass SourcePoJo{ private String username; private Long id;}
@Dataclass TargetPoJo{ private String username; private long id;}
Im Testfall verwendet das ID-Feld den Wrapper-Typ und den Basistyp in der Kopierquelle bzw. dem Kopierziel. Sie können sehen, dass beim Kopieren eine Ausnahme aufgetreten ist.

Hinweis: Wenn ein boolesches Attribut einen Basistyp bzw. einen Wrapper-Typ verwendet und der Attributname mit „is“ beginnt, z. B. „isSuccess“, schlägt auch der Kopiervorgang fehl.

2: Das Überschreiben von Nullwerten führt zu Datenanomalien

Während der Geschäftsentwicklung müssen wir möglicherweise einige Felder kopieren . Wenn einige Felder in den kopierten Daten Nullwerte haben, die Werte derselben Felder in den entsprechenden Daten, die kopiert werden müssen, jedoch nicht Null sind, wenn direkt BeanUtils.copyProperties wird zum Kopieren von Daten verwendet. Der Nullwert der kopierten Daten überschreibt die Felder der kopierten Zieldaten, wodurch die Originaldaten ungültig werden.
Die entsprechenden Fälle sind wie folgt:
  
  
  
  
  
public class BeanUtilsTest {
public static void main(String[] args) { SourcePoJo sourcePoJo = new SourcePoJo(); sourcePoJo.setId("35711"); TargetPoJo targetPoJo = new TargetPoJo(); targetPoJo.setUsername("Joy"); BeanUtils.copyProperties(sourcePoJo,targetPoJo); System.out.println(targetPoJo); }}@Dataclass SourcePoJo{ private String username; private String id;}
@Dataclass TargetPoJo{ private String username; private String id;}
Die entsprechenden Laufergebnisse lauten wie folgt:

Sie können sehen, dass das Feld „Benutzername“, das ursprünglich einen Wert im Kopierzielergebnis hatte, mit Null überschrieben wurde. Obwohl Sie die überladene Methode von BeanUtils.copyProperties verwenden und das benutzerdefinierte ConvertUtilsBean zum Kopieren einiger Felder verwenden können, ist dies selbst komplizierter und verliert die Bedeutung der Verwendung von BeanUtils.copyProperties zum Kopieren von Daten. Daher wird dies nicht empfohlen.

3: Ein Paketimportfehler führt zu abnormalen Kopierdaten

Wenn Sie BeanUtils.copyProperties zum Kopieren von Daten verwenden und sowohl das Bean-Paket von Spring als auch das Beanutils-Paket von Apache in das Projekt eingeführt werden und beim Importieren des Pakets ein Importfehler auftritt, schlägt die Datenkopie wahrscheinlich fehl, was nicht einfach ist bei der Fehlerbehebung zu erkennen. Wir verwenden normalerweise die Kopiermethode im Sping-Paket. Die Unterschiede zwischen den beiden sind wie folgt:
  
  
  
  
  
//org.springframework.beans.BeanUtils(源对象在左边,目标对象在右边)public static void copyProperties(Object source, Object target) throws BeansException //org.apache.commons.beanutils.BeanUtils(源对象在右边,目标对象在左边)public static void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException

4: Die Feldreferenz kann nicht gefunden werden und der geänderte Inhalt ist schwer zu verfolgen.

Wenn wir während der Entwicklung oder Fehlerbehebung die Quelle eines Feldwerts ( nicht vom Aufrufer übergeben ) im Link finden, verwenden wir möglicherweise die Volltextsuche, um die entsprechende Zuweisungsmethode (z. B. Set-Methode, Build-Methode usw.) zu finden. , aber wenn die Daten mithilfe von BeanUtils.copyProperties im Link kopiert werden, ist es schwierig, die Stelle, an der der Wert zugewiesen ist, schnell zu finden, was zu einer geringen Effizienz bei der Fehlerbehebung führt.

5: Interne Klassendaten können nicht erfolgreich kopiert werden

Interne Klassendaten können nicht normal kopiert werden. Auch wenn Typ und Feldname identisch sind, kann das Kopieren nicht erfolgreich sein, wie unten gezeigt:
  
  
  
  
  
public class BeanUtilsTest {
public static void main(String[] args) { SourcePoJo sourcePoJo = new SourcePoJo(); sourcePoJo.setUsername("joy"); SourcePoJo.InnerClass innerClass = new SourcePoJo.InnerClass("sourceInner"); sourcePoJo.innerClass=innerClass; System.out.println(sourcePoJo.toString()); TargetPoJo targetPoJo = new TargetPoJo(); BeanUtils.copyProperties(sourcePoJo,targetPoJo); System.out.println(targetPoJo.toString()); }}//下面是类的信息,这里就直接放到一块展示了@Data@ToStringpublic class SourcePoJo{ private String username; private Long id; public InnerClass innerClass; @Data @ToString @AllArgsConstructor public static class InnerClass{ public String innerName; }}
@Data@ToStringpublic class TargetPoJo{ private String username; private Long id; public InnerClass innerClass; @Data @ToString public static class InnerClass{ public String innerName; }}
Hier sind die Ergebnisse:

Im obigen Fall gibt es in der Kopierquelle und im Kopierziel eine innere Klasse InnerClass. Obwohl die inneren Klassenattribute gleich sind und der Klassenname gleich ist, befinden sie sich in unterschiedlichen Klassen, sodass Spring denkt, dass die Attribute unterschiedlich sind und kopiert die Daten nicht. .

6: BeanUtils.copyProperties ist eine flache Kopie

Hier werde ich zunächst Deep Copy und Shallow Copy für alle besprechen.
Unter flachem Kopieren versteht man das Erstellen eines neuen Objekts, das dieselben Eigenschaftswerte wie das ursprüngliche Objekt hat, aber dennoch dieselbe Referenz für Eigenschaften von Referenztypen aufweist. Das heißt, wenn sich bei flacher Kopie der Referenzattributwert des Originalinhalts ändert, ändert sich auch der Referenzattributwert des kopierten Objekts entsprechend.
Eine tiefe Kopie bedeutet, dass ein neues Objekt mit denselben Eigenschaftswerten wie das Originalobjekt erstellt wird, einschließlich der Eigenschaften von Referenztypen. Deep Copy kopiert das Referenzobjekt rekursiv und erstellt ein brandneues Objekt, sodass das kopierte Objekt völlig unabhängig vom Originalobjekt ist.
Das Folgende ist das entsprechende Codebeispiel:
  
  
  
  
  
public class BeanUtilsTest {
public static void main(String[] args) { Person sourcePerson = new Person("sunyangwei",new Card("123456")); Person targetPerson = new Person(); BeanUtils.copyProperties(sourcePerson, targetPerson); sourcePerson.getCard().setNum("35711"); System.out.println(targetPerson); }}

@Data@AllArgsConstructorclass Card { private String num;}
@NoArgsConstructor@AllArgsConstructor@Dataclass Person { private String name; private Card card;}
Hier sind die Ergebnisse:
Zusammenfassung: Anhand der Ergebnisse der Codeausführung können wir feststellen, dass eine Änderung der Referenztypdaten des Originalobjekts nach dem Kopieren dazu führt, dass der Wert der kopierten Daten abnormal ist. Diese Art von Problem ist ebenfalls schwer zu beheben.

7: Die zugrunde liegende Implementierung ist eine reflektierende Kopie mit geringer Effizienz

Die unterste Ebene von BeanUtils.copyProperties besteht darin, die Set- und Get-Methoden des Objekts durch Reflektion abzurufen und dann das Kopieren der Daten durch Get und Set abzuschließen. Die Gesamteffizienz des Kopierens ist gering.
Das Folgende ist ein Vergleich der Effizienz des Kopierens von Daten mit BeanUtils.copyProperties und dem direkten Festlegen des Werts. Um den Effekt visuell zu sehen, finden Sie hier ein Beispiel für das 10.000-fache Kopieren:
  
  
  
  
  
public class BeanUtilsTest {
public static void main(String[] args) { long copyStartTime = System.currentTimeMillis(); User sourceUser = new User("sunyangwei"); User targetUser = new User(); for(int i = 0; i < 10000; i++) { BeanUtils.copyProperties(sourceUser, targetUser); } System.out.println("copy方式:"+(System.currentTimeMillis()-copyStartTime));
long setStartTime = System.currentTimeMillis(); for(int i = 0; i < 10000; i++) { targetUser.setUserName(sourceUser.getUserName()); } System.out.println("set方式:"+(System.currentTimeMillis()-setStartTime)); }}
@Data@AllArgsConstructor@NoArgsConstructorclass User{ private String userName;}
Im Folgenden finden Sie einen Vergleich der Ergebnisse zur Ausführungseffizienz:
 Es kann festgestellt werden , dass der Leistungsunterschied zwischen herkömmlichen Sätzen und BeanUtils.copyProperties sehr groß ist. Verwenden Sie BeanUtils.copyProperties daher mit Vorsicht.
Die oben genannten sind häufige Fallstricke bei der Verwendung von BeanUtils.copyProperties zum Kopieren von Daten. Die meisten dieser Fallstricke sind relativ versteckt und bei Problemen schwer zu beheben. Daher wird die Verwendung von BeanUtils.copyProperties zum Kopieren von Daten im Unternehmen nicht empfohlen. Eventuelle Mängel im Artikel können gerne ergänzt und korrigiert werden.
-Ende-

Dieser Artikel wurde über das öffentliche WeChat-Konto geteilt – JD Cloud Developers (JDT_Developers).
Bei Verstößen wenden Sie sich bitte zur Löschung an [email protected].
Dieser Artikel ist Teil des „ OSC Source Creation Plan “. Wer liest, ist herzlich eingeladen, mitzumachen und gemeinsam zu teilen.

博通宣布终止现有 VMware 合作伙伴计划 B站崩了两次、腾讯“3.29”一级事故……盘点 2023 十大宕机事故“冥场面” Vue 3.4 “灌篮高手”发布 养乐多公司确认 95 G 数据被泄露 MySQL 5.7、魔趣、李跳跳……盘点 2023“停更”的(开源)项目和网站 《2023 中国开源开发者报告》正式发布 回顾 30 年前的 IDE:只有 TUI、背景颜色亮瞎眼…… Julia 1.10 正式发布 Rust 1.75.0 发布 英伟达推出特供中国销售的 GeForce RTX 4090 D
{{o.name}}
{{m.name}}

Supongo que te gusta

Origin my.oschina.net/u/4090830/blog/10555360
Recomendado
Clasificación