在本文中,我将解釋如何在現代Java應用程式中使用DTO,應用程式可以從中受益的方式,以及Java DTO如何通過防止意外資料洩漏來幫助您提高安全性。
Java中的資料傳輸對象(DTO)是在子系統之間傳輸資料的對象。它是一種用于聚合資料的企業設計模式。主要目的是減少子系統之間所需的系統調用次數,進而減少所産生的開銷。
在本文中,我将解釋如何在現代Java應用程式中使用DTO,應用程式可以從中受益的方式,以及Java DTO如何通過防止意外資料洩漏來幫助您提高安全性。
一.什麼是POJO、Java Bean和值對象
正如名稱所暗示的那樣,普通舊Java對象(Plain Old Java Object,POJO)是一個普通的Java對象。它可以是任何類,并且除了Java語言規定的限制外,不受任何特定限制的限制。建立它們是為了可重用性和增加可讀性。
public class CoffeePOJO {
public String name;
private List<String> ingredients;
public CoffeePOJO(String name, List<String> ingredients) {
this.name = name;
this.ingredients = ingredients;
}
void addIngredient(String ingredient) {
ingredients.add(ingredient);
}
}
Java Bean是一個POJO,根據JavaBean标準根據這個标準,所有的屬性都是私有的,并且可以用getter和setter方法通路。另外,應該有一個無參數的構造函數,沿着一些其他的東西。
是以,這意味着雖然所有的Java Bean都是POJO,但并非所有的POJO都是Java Bean。
public class CoffeeBEAN implements Serializable {
private String name;
private List<String> ingredients;
public CoffeeBEAN() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getIngredients() {
return ingredients;
}
public void setIngredients(List<String> ingredients) {
this.ingredients = ingredients;
}
}
值對象是表示簡單資料實體的小對象。但是,值對象通常沒有辨別。值對象目前在Java中不可用,但是JDK維護人員正在努力添加它們作為JEP 401的一部分。
實作資料傳輸對象
DTO可以實作為POJO,也可以實作為JavaBean。最重要的是,DTO将實體(例如表示層和域模型)之間的關注點分開。
讓我們用一個小的休息服務來解釋這一點。假設我們有一個咖啡店,裡面有咖啡和顧客。這兩者在系統中都是獨立的域實體。如果我想知道顧客最喜歡的咖啡,我将建立一個API,它提供FavoriteCoffeDTO中表示的聚合資料。
代碼如下所示:
public class Coffee {
private Long id;
private String name;
private List<String> ingredients;
private String preparation;
}
public class Customer {
private Long id;
private String firstName;
private String lastName;
private List<Coffee> coffees;
}
public class FavoriteCoffeeDTO {
private String name;
private List<String> coffees;
}
在上面的實作中,我将域層與表示層分離,現在允許我的控制器處理将兩個域實體映射到DTO。
在本例中,我将字段設定為private,這意味着我必須建立getter和setter方法來通路字段。使用者通常選擇完全或部分遵循JavaBean标準來通路DTO。當然,這不是強制性的,您可以自由選擇适合您需要的方法。其他選項包括将所有字段設定為public和直接通路它們(如下面的示例),或者使用全參數構造函數和一些getter方法使對象不可變。
public class FavoriteCoffeeDTO {
public String name;
public List<String> coffees;
}
String name = favCoffeeDTO.name;
最後,如果更新到較新版本的Java,則可能希望對DTO使用Java記錄。Java記錄是簡單的不可變類,它們自動為您提供全參數構造函數、通路方法、toString()和hashCode()而不定義它們。這會使代碼更簡潔、更易讀。請注意,記錄不遵循JavaBean規範,因為通路方法沒有get 或set 字首。
public record FavoriteCoffeeDTO(String name,List<String> coffees){}
String name=favoriteCoffeeDTO.name();
什麼是好的DTO?
DTO的用途是在程序之間傳送資料。是以,一個好的DTO隻包含系統的特定部分所需的資訊。在我們的API示例中,我們隻需要傳回客戶的姓名和他們最喜歡的咖啡訂單。不需要添加任何其他内容,如業務邏輯。一般建議是使DTO盡可能簡單、小和直接。
另外,DTO初始化後,其狀态不應改變或演變。這意味着不可變的資料結構将非常适合DTO。由于DTO隻攜帶不應改變的資料,Java Record将非常适合-特别是因為JSON序列化庫支援Java Record。
DTO安全注意事項
我們已經注意到,我們使用這個DTO模式将域模型從表示層中分離出來。簡單的DTO隻包含這個子系統或API所需的資料,不包含任何業務邏輯,也可以提高安全性。
我們在許多概念證明中看到的是域條目被完全輸出。這可能導緻不必要的資料在系統之外可用,并可能導緻資料洩漏。
假設我們的API有一個查找所有客戶的函數,但不使用DTO:
@GetMapping("/customers")
public List<Customer> getAllCustomers(){
return repository.findAll();
}
如果我們的customer對象與上一個示例中的相同,那麼我們已經顯示了太多的資訊。id 或收藏夾清單coffees?如果我們決定為客戶附加更多資訊(如家庭住址),情況會變得更糟。
public class Customer {
private Long id;
private String firstName;
private String lastName;
private List<Coffee> coffees;
Private String
homeAddress;
}
如果我們不過濾我們的端點,我們會突然造成資料洩露,因為提供全名和家庭位址在許多國家都被認為是侵犯隐私。将API與資料模型和DTO分離可以防止這種情況,因為映射器控制器隻會填充DTO中的必要字段。即使我們決定在以後向域模型中添加一些内容,使用DTO可以防止我們洩露個人身份資訊。
public class CustomerDTO {
private String firstName;
private String lastName;
}
二.建議
在Java中使用DTO來分離子系統通常是一個好主意。從工程的角度來看,這将減少不同層之間的往返,并形成一個安全角度,這将有助于防止意外的資料洩漏。在安全性方面,我建議使您的DTO具體化,并限制重用的數量。如果您為不同的功能重用DTO,您應該在更改這些DTO之前清楚地了解它們的使用位置。
一般來說,保持DTO簡潔,如果可能的話,不包含業務邏輯,并且隻提供特定功能所需的資料。最後,我相信不變性是DTO的天然選擇,這使得Java Records(在Java 16中完全釋出)成為在Java中實作DTO的一種很好的方式。
請檢視以下資源以了解有關Java安全性的更多資訊:
· Java中的序列化和反序列化:解釋Java反序列化漏洞
· 使用Docker建構Java容器的10個最佳實踐
· 管理Java依賴關系的最佳實踐
· 10個Java安全最佳實踐
· Java日志記錄:您應該記錄什麼,不應該記錄什麼?
三.常見問題解答
什麼是DTO?
DTO代表Data Transfer Object。它是在應用程式中的不同程序之間傳輸資料的對象。DTO專門設計用于通過聚集資料來減少客戶機和伺服器之間的方法調用次數。它還可以防止實體之間的緊密耦合,例如應用程式的域模型和表示層。
什麼是Java中的DTO?
Java中的資料傳輸對象通常是沒有任何業務邏輯的POJO(Plain Old Java Object)。它們通常有一個平面資料結構,特别适合它所用于的方法之間的事務的目的。Java中的DTO可能遵循JavaBean約定,帶有私有字段和getter和setter方法,但它沒有這樣做。
DTO是否與POJO相同?
簡而言之,DTO是POJO,但不是每個POJO都是DTO。資料傳輸對象是在程序之間傳輸資料的特定POJO。
使用DTO是一種好的做法嗎?
是的,如果DTO實作得很好,它們可以減少緊密耦合和方法調用,進而提高安全性并減少資料洩漏。
DAO和DTO之間的差別是什麼?
DAO代表資料通路對象,在一種模式中用于将業務邏輯與持久層分離。DAO通常用于CRUD操作,如更新、删除和儲存。DTO是一個專門用于在子系統之間傳輸資料的對象,不應包含業務邏輯。