使用 Java @Annotations 構(gòu)建完整的 Spring Boot REST API
本文旨在演示用于構(gòu)建功能性 Spring Boot REST API 的重要 Java @annotations。Java 注解的使用使開發(fā)人員能夠通過簡單的注解來減少代碼冗長。
例如,我們可以參考交易。通過使用事務(wù)模板的標(biāo)準(zhǔn)程序化處理,這需要編寫更復(fù)雜的配置和樣板代碼,而這可以通過簡單的@Transactional 聲明性注釋來實(shí)現(xiàn)。
在 Java 編程語言中,注解是一種語法元數(shù)據(jù),可以添加到 Java 源代碼中。Java 注釋也可以嵌入到 Java 編譯器生成的 Java 類文件中并從中讀取。這允許 Java 虛擬機(jī)在運(yùn)行時(shí)保留注釋并通過反射讀取。對注解的支持從版本 5 開始,允許不同的 Java 框架采用這些資源。
注釋也可以在 REST API 中使用。REST 代表 Representational State Transfer,是一種用于設(shè)計(jì)分布式應(yīng)用程序的架構(gòu)風(fēng)格。由 Roy Fielding 博士帶來。在論文中,他提出了客戶端和服務(wù)器之間應(yīng)該分開的六項(xiàng)原則;客戶端和服務(wù)器之間的通信應(yīng)該是無狀態(tài)的;它們之間可以存在多個(gè)層次結(jié)構(gòu);服務(wù)器響應(yīng)必須聲明為可緩存或不可緩存;其接口的統(tǒng)一性必須基于客戶端、服務(wù)器和中間組件之間的所有交互,最終客戶端可以通過按需使用代碼來擴(kuò)展其功能。
1
API 是一個(gè)簡單的模塊,用于從更復(fù)雜的系統(tǒng)中實(shí)現(xiàn)業(yè)務(wù)實(shí)體的 CRUD 操作,旨在協(xié)調(diào)和協(xié)調(diào)與企業(yè)、機(jī)構(gòu)和實(shí)體組相關(guān)的經(jīng)濟(jì)信息。為簡單起見,API 使用 H2 內(nèi)存數(shù)據(jù)庫。

項(xiàng)目結(jié)構(gòu)
項(xiàng)目結(jié)構(gòu)由三個(gè)模塊構(gòu)成,但本文將重點(diǎn)介紹管理實(shí)體的模塊。該模塊依賴于 Common 模塊,它與整個(gè)系統(tǒng)的其余部分共享錯(cuò)誤處理和必要的有用類等內(nèi)容。示例代碼可從 GitHub 存儲(chǔ)庫訪問。
https://github.com/jailsonevora/spring-boot-api-communication-through-kafka
讓我們開始吧。
2
Spring Boot 的巨大優(yōu)勢在于我們可以專注于業(yè)務(wù)規(guī)則,從而避免一些繁瑣的開發(fā)步驟、樣板代碼和更復(fù)雜的配置,從而改進(jìn)開發(fā)并簡化新 Spring 應(yīng)用程序的引導(dǎo)。
為了開始配置新的 Spring Boot 應(yīng)用程序,Spring Initializr 創(chuàng)建了一個(gè)簡單的 POJO 類來配置應(yīng)用程序的初始化。我們有兩種方式來裝飾配置。一種是@SpringBootApplication當(dāng)我們的解決方案中的模塊較少時(shí)使用注釋。
如果我們有一個(gè)結(jié)構(gòu)更復(fù)雜的解決方案,我們需要將不同的路徑或我們模塊的基本包指定給 Spring Boot 應(yīng)用程序初始化程序類。@EnableAutoConfiguration我們可以使用和@ComponentScan注釋來實(shí)現(xiàn)它。@EnableAutoConfiguration指示 Spring Boot 根據(jù)類路徑設(shè)置、其他 bean 和各種屬性設(shè)置開始添加 bean,同時(shí)@ComponentScan允許 spring 在包中查找其他組件、配置和服務(wù),讓它在其他組件中找到控制器。
這兩個(gè)注解不能同時(shí)使用@SpringBootApplication。@SpringBootApplication是添加所有這些的便利注釋。它等同于使用@Configuration, @EnableAutoConfiguration, 和@ComponentScan它們的默認(rèn)屬性。
package?com.BusinessEntityManagementSystem;
import?org.springframework.boot.SpringApplication;
import?org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import?org.springframework.boot.autoconfigure.domain.EntityScan;
import?org.springframework.context.annotation.ComponentScan;
import?org.springframework.context.annotation.Configuration;
import?org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import?org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@EnableAutoConfiguration
@EnableJpaAuditing
@EnableJpaRepositories(basePackages = {"com.BusinessEntityManagementSystem"})
@EntityScan(basePackages = {"com.BusinessEntityManagementSystem"})
@ComponentScan(basePackages = {"com.BusinessEntityManagementSystem"})
@Configuration
public?class?BusinessEntityManagementApplication?{
????public?static?void?main(String[] args)?{
????????SpringApplication.run(BusinessEntityManagementApplication.class, args);
????}
}代碼片段中的另一個(gè)注釋是@EnableJpaAuditing. 審計(jì)允許系統(tǒng)跟蹤和記錄與持久實(shí)體或?qū)嶓w版本相關(guān)的事件。還與 JPA 配置相關(guān),我們有@EnableJpaRepositories. 此注釋啟用 JPA 存儲(chǔ)庫。默認(rèn)情況下,它將掃描帶注釋的配置類的包以查找 Spring Data 存儲(chǔ)庫。在這個(gè)注解中,我們指定要掃描注解組件的基本包。
要在項(xiàng)目結(jié)構(gòu)中查找 JPA 實(shí)體,我們必須指示自動(dòng)配置使用@EntityScan掃描包。對于特定的掃描,我們可以指定basePackageClasses(),basePackages()或其別名value()來定義要掃描的特定包。如果未定義特定的包,則會(huì)從帶有此注解的類的包中進(jìn)行掃描。
Spring Boot Initializr 創(chuàng)建的類中的最后一個(gè)注解是@Configuration. @Configuration將類標(biāo)記為應(yīng)用程序上下文的 bean 定義源。這可以應(yīng)用于我們需要的任何配置類。
?
3
文檔是任何項(xiàng)目的一個(gè)重要方面,因此我們的 REST API 使用 Swagger-UI 進(jìn)行記錄,這是許多標(biāo)準(zhǔn)元數(shù)據(jù)之一。Swagger 是用于創(chuàng)建交互式 REST API 文檔的規(guī)范和框架。它使文檔能夠與對 REST 服務(wù)所做的任何更改保持同步。它還提供了一組工具和 SDK 生成器,用于生成 API 客戶端代碼。
在 Swagger-UI 類配置中,出現(xiàn)在@Configuration. 如上所述,這向 Spring Boot 自動(dòng)配置表明一個(gè)類是一個(gè)可能包含 bean 定義的配置類。
package?com.BusinessEntityManagementSystem;
import?org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import?org.springframework.context.annotation.Bean;
import?org.springframework.context.annotation.Configuration;
import?springfox.documentation.builders.PathSelectors;
import?springfox.documentation.builders.RequestHandlerSelectors;
import?springfox.documentation.service.ApiInfo;
import?springfox.documentation.service.Contact;
import?springfox.documentation.spi.DocumentationType;
import?springfox.documentation.spring.web.plugins.Docket;
import?springfox.documentation.swagger2.annotations.EnableSwagger2;
import?java.util.Collections;
@Configuration
@EnableSwagger2
@EnableAutoConfiguration
public?class?SwaggerConfig?{
????@Bean
????public?Docket api()?{
????????return?new?Docket(DocumentationType.SWAGGER_2)
????????????????.select()
????????????????.apis(RequestHandlerSelectors.basePackage("com.BusinessEntityManagementSystem"))
????????????????.paths(PathSelectors.ant("/api/businessEntityManagementSystem/v1/**"))
????????????????.build()
????????????????.apiInfo(metaData());
????}
...Swagger 的一個(gè)特定注釋是@EnableSwagger2。它表明應(yīng)該啟用 Swagger 支持并加載所有在 swagger java-config 類中定義的必需 bean。這應(yīng)該應(yīng)用于 Spring java 配置,并且應(yīng)該有一個(gè)隨附的@Configuration注釋。@Bean是方法級(jí)別的注釋,是 XML 元素的直接模擬
?
4
MVC 是 Spring Framework 中最重要的模塊之一。它是UI設(shè)計(jì)中常見的設(shè)計(jì)模式。它通過分離模型、視圖和控制器的角色將業(yè)務(wù)邏輯與 UI 分離。MVC 模式的核心思想是將業(yè)務(wù)邏輯從 UI 中分離出來,允許它們獨(dú)立更改而不相互影響。
在此設(shè)計(jì)模式中,M 代表模型。該模型負(fù)責(zé)封裝應(yīng)用程序數(shù)據(jù)以供視圖呈現(xiàn)。它代表了數(shù)據(jù)和業(yè)務(wù)邏輯的形狀。模型對象檢索模型狀態(tài)并將其存儲(chǔ)在數(shù)據(jù)庫中。它的模型通常由服務(wù)層處理并由持久層持久化的領(lǐng)域?qū)ο蠼M成。
TYPE Java @Annotations
在模型類中,我們使用@Entity注釋來指示該類是 JPA 實(shí)體。JPA 將知道 POJO 類可以存儲(chǔ)在數(shù)據(jù)庫中。如果我們沒有定義@Table注解,Spring config 將假定這個(gè)實(shí)體被映射到一個(gè)類似于 POJO 類名的表。因此,在這些情況下,我們可以使用@Table注解指定表名。
當(dāng)模型屬性定義了延遲加載時(shí),為了處理與使用 Jackson API 進(jìn)行模型序列化相關(guān)的問題,我們必須告訴序列化器忽略 Hibernate 添加到類中的鏈或有用的垃圾,以便它可以管理延遲加載通過聲明@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})注釋來獲取數(shù)據(jù)。
package?com.BusinessEntityManagementSystem.models;
import?com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import?com.BusinessEntityManagementSystem.interfaces.models.IBusinessEntityModel;
import?org.hibernate.annotations.OnDelete;
import?org.hibernate.annotations.OnDeleteAction;
import?javax.persistence.*;
import?javax.validation.constraints.NotNull;
import?javax.validation.constraints.Size;
import?java.io.Serializable;
import?java.util.HashSet;
import?java.util.Set;
@Entity
@Table(name="BEMS_BusinessEntity")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public?class?BusinessEntityModel<IAddressModel?extends?AddressModel, IPartnerModel?extends?PartnerModel, IStoreModel?extends?StoreModel, IAffiliatedCompanyModel?extends?AffiliatedCompanyModel, IEconomicActivityCodeModel?extends?EconomicActivityCodeModel> extends?AuditModel<String> implements?IBusinessEntityModel, Serializable?{
????@Id
????@GeneratedValue(strategy = GenerationType.AUTO)
????@NotNull
????@Column(name = "ID_BusinessEntity", updatable = false, nullable = false)
????private?long?id;
...FIELD Java @Annotations
對于一個(gè)類字段,有多種注解取決于該字段的類型和用途。例如,@Id注釋必須在類屬性之一中聲明。存儲(chǔ)在數(shù)據(jù)庫中的每個(gè)實(shí)體對象都有一個(gè)主鍵。一旦分配,主鍵就不能被修改。@GeneratedValue指示框架應(yīng)使用指定的生成器類型(如 {AUTO、IDENTITY、SEQUENCE 和 TABLE})生成文檔鍵值。
另一個(gè)針對域模型字段的有趣注釋是@NotNull. 聲明帶注釋的元素不能是常見的 Spring 注釋null。它也可以用在方法或參數(shù)中。注釋指定數(shù)據(jù)庫列的@Column名稱以及表行為。可以設(shè)置此行為以防止其被更新或?yàn)榭铡?/span>
有時(shí)大多數(shù)對象都有一個(gè)自然標(biāo)識(shí)符,因此 Hibernate 還允許將此標(biāo)識(shí)符建模為實(shí)體的自然標(biāo)識(shí)符,并提供額外的 API 用于從數(shù)據(jù)庫中檢索它們。這是使用@NaturalId注釋來實(shí)現(xiàn)的。
...
????@Id
????@GeneratedValue(strategy = GenerationType.AUTO)
????@NotNull
????@Column(name = "ID_BusinessEntity", updatable = false, nullable = false)
????private?long?id;
????@NaturalId
????@NotEmpty
????@NotNull
????@Column(name = "NATURAL_ID", updatable = false, nullable = false)
????private long naturalId;
????@Column(name = "TaxId")
????@Size(min = 1, max = 50)
????private String taxId;
????@Column(name = "Status")
????private int status = 1;
...如果我們想防止一個(gè)實(shí)體的元素不為空也不為空,我們也可以用 注釋它@NotEmpty。它針對大量元素,因?yàn)閧METHOD,FIELD,ANNOTATION_TYPE,CONSTRUCTOR,PARAMETER,TYPE_USE}. @Size注釋劃定了被注釋元素的邊界。邊界由兩個(gè)屬性 min 和 max 指定。
關(guān)系 Java @Annotations
任何 ORM 機(jī)制最重要的特性之一是如何指定從對象之間的關(guān)系到其數(shù)據(jù)庫對應(yīng)項(xiàng)的映射。在下面的代碼中,有一個(gè)@OneToOne注解來描述BusinessEntity類與Address類模型之間的關(guān)系。@JoinColumn注釋指定在此關(guān)系中將被視為外鍵的列。
除了@OneToOne注釋,我們還可以管理多對多關(guān)系。@ManyToMany注釋描述了與Partner類成員的關(guān)系。與其他關(guān)系注釋一樣,也可以指定級(jí)聯(lián)規(guī)則以及獲取類型。根據(jù)所選擇的級(jí)聯(lián)設(shè)置,當(dāng)BusinessEntity刪除 a 時(shí),關(guān)聯(lián)的Partner也將被刪除。
...
????// region OneToOne
????@OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL, targetEntity= AddressModel.class)
????@JoinColumn(name = "Address", nullable = false)
????@OnDelete(action = OnDeleteAction.CASCADE)
????private?IAddressModel?address;
????// endregion OneToOne
????// region ManyToMany
????@ManyToMany(fetch = FetchType.LAZY,
????????????cascade = {
????????????????????CascadeType.ALL
????????????},
????????????targetEntity= PartnerModel.class
????)
????@JoinTable(name = "BSUF_BusinessEntity_Partner",
????????????joinColumns = { @JoinColumn(name = "ID_BusinessEntity") },
????????????inverseJoinColumns = { @JoinColumn(name = "ID_Partner") })
????private Set partner = new HashSet<>();
// endregion ManyToMany
... 與@ManyToMany注釋一起,我們指定@JoinTable注釋,允許我們在多對多關(guān)系中使用兩個(gè)基本屬性joincolumns為我們聲明@ManyToMany注釋的類和inverseJoinColumns另一個(gè)表定義其他兩個(gè)相關(guān)表之間的橋接表。
...
??// inverser many to many
??@ManyToMany(fetch = FetchType.LAZY,
??????????????cascade = {
????????????????CascadeType.PERSIST,
????????????????CascadeType.MERGE
??????????????},
??????????????mappedBy = "partner")
??@JsonIgnore
??private?Set businessEntity = new?HashSet<>();
... 在另一個(gè)表中,建議也定義逆關(guān)系。此聲明與與業(yè)務(wù)實(shí)體模型相關(guān)的代碼中顯示的內(nèi)容略有不同。反向關(guān)系聲明通過屬性“ mappedBy. ”來區(qū)分。
5
數(shù)據(jù)傳輸對象是一種非常流行的設(shè)計(jì)模式。它是一個(gè)定義數(shù)據(jù)如何通過網(wǎng)絡(luò)發(fā)送的對象。DTO 僅用于傳遞數(shù)據(jù),不包含任何業(yè)務(wù)邏輯。
TYPE Java @Annotations
有時(shí),我們需要通過 JSON 在實(shí)體之間傳輸數(shù)據(jù)。要序列化和反序列化 DTO 對象,我們需要使用 Jackson 注釋對這些對象進(jìn)行注釋。
@JsonInclude(JsonInclude.Include.NON_NULL)指示何時(shí)可以序列化帶注釋的屬性。通過使用這個(gè)注解,我們可以根據(jù)屬性值指定簡單的排除規(guī)則。它可以用于字段、方法或構(gòu)造函數(shù)參數(shù)。它也可以用在類中,在某些情況下,指定的規(guī)則適用于類的所有屬性。
package?com.BusinessEntityManagementSystem.dataTransferObject;
import?com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import?com.fasterxml.jackson.annotation.JsonProperty;
/*@JsonInclude(JsonInclude.Include.NON_NULL)*/
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public?class?BusinessEntity?{
...@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})允許 Jackson 忽略 Hibernate 創(chuàng)建的垃圾,因此它可以管理前面提到的數(shù)據(jù)的延遲加載。
FIELD Java @Annotations
DTO 對象中的字段也可能具有不同類型的注釋。@JsonProperty注釋用于指定序列化屬性的名稱。@JsonIgnore在類屬性級(jí)別進(jìn)行注釋以忽略它。除了@JsonIgnore,還有@JsonIgnoreProperties和@JsonIgnoreType。這兩個(gè)注釋都是 Jackson API 的一部分,用于忽略 JSON 序列化和反序列化中的邏輯屬性。
package?com.BusinessEntityManagementSystem.dataTransferObject;
import?com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import?com.fasterxml.jackson.annotation.JsonProperty;
/*@JsonInclude(JsonInclude.Include.NON_NULL)*/
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public?class?BusinessEntity?{
????@JsonProperty("naturalId")
????private?long?naturalId;
????@JsonProperty("taxId")
????private?String taxId;
????@JsonProperty("status")
????private?int?status = 1;
...Jackson API 是用于 Java 的高性能 JSON 處理器。它提供了許多有用的注釋來應(yīng)用于 DTO 對象,允許我們將對象從 JSON 序列化和反序列化為 JSON。
6
控制器代表 MVC 模式中的 C。控制器負(fù)責(zé)接收用戶的請求并調(diào)用后端服務(wù)進(jìn)行業(yè)務(wù)處理。處理后,它可能會(huì)返回一些數(shù)據(jù)以供視圖呈現(xiàn)。控制器收集它并準(zhǔn)備模型以供視圖呈現(xiàn)。控制器通常稱為調(diào)度程序 servlet。它作為 Spring MVC 框架的前端控制器,每個(gè) Web 請求都必須經(jīng)過它,以便它可以管理整個(gè)請求處理過程。當(dāng)一個(gè) Web 請求被發(fā)送到 Spring MVC 應(yīng)用程序時(shí),控制器首先接收該請求。然后,它組織在 Spring 的 Web 應(yīng)用程序上下文中配置的不同組件或控制器本身中存在的注釋,所有這些都需要處理請求。
TYPE Java @Annotations
要在 Spring Boot 中定義控制器類,必須用@RestController注解標(biāo)記類。@RestController注解是一個(gè)便利注解,它本身用@Controller和@ResponseBody注解。帶有此注解的類型被視為控制器,其中@RequestMapping方法默認(rèn)采用@ResponseBody語義。value 屬性可以指示對邏輯組件名稱的建議,以在自動(dòng)檢測到組件的情況下將其轉(zhuǎn)換為 Spring bean。
package?com.BusinessEntityManagementSystem.v1.controllers;
import?com.BusinessEntityManagementSystem.dataAccessObject.IBusinessEntityRepository;
import?com.BusinessEntityManagementSystem.interfaces.resource.IGenericCRUD;
import?com.BusinessEntityManagementSystem.models.BusinessEntityModel;
import?com.Common.Util.Status;
import?com.Common.exception.ResourceNotFoundException;
import?io.swagger.annotations.Api;
import?io.swagger.annotations.ApiOperation;
import?org.springframework.beans.factory.annotation.Autowired;
import?org.springframework.data.domain.Page;
import?org.springframework.data.domain.Pageable;
import?org.springframework.http.HttpHeaders;
import?org.springframework.http.HttpStatus;
import?org.springframework.http.ResponseEntity;
import?org.springframework.web.bind.annotation.*;
import?org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import?javax.persistence.EntityManager;
import?javax.persistence.PersistenceContext;
import?javax.transaction.Transactional;
import?javax.validation.Valid;
import?java.util.Optional;
@RestController("BusinessEntityV1")
@RequestMapping("/api/businessEntityManagementSystem/v1")
@Api(value = "businessEntity")
public class BusinessEntityController implements IGenericCRUD {
... 構(gòu)造函數(shù)和方法 Java @Annotations
當(dāng)帶有@RestController 注釋的類收到請求時(shí),它會(huì)尋找適當(dāng)?shù)奶幚沓绦蚍椒▉硖幚碚埱蟆_@要求控制器通過一個(gè)或多個(gè)處理程序映射將每個(gè)請求映射到處理程序方法。為此,控制器類的方法用@RequestMapping注解修飾,使它們成為處理方法。
出于 Swagger 文檔的目的,@ApiOperation注釋用于聲明 API 資源中的單個(gè)操作。操作被認(rèn)為是路徑和 HTTP 方法的唯一組合。只有帶有注釋的方法@ApiOperation才會(huì)被掃描并添加到 API 聲明中。一些處理程序或操作需要使用事務(wù)來確保數(shù)據(jù)完整性和一致性。
事務(wù)管理是企業(yè)應(yīng)用程序中確保數(shù)據(jù)完整性和一致性的一項(xiàng)基本技術(shù)。Spring 支持編程式和聲明式(@Transactional)事務(wù)管理。
...
?@Autowired
????public?BusinessEntityController(IBusinessEntityRepository businessEntityRepository) {
????????this.businessEntityRepository = businessEntityRepository;
????}
????@RequestMapping(value = "/businessEntity/{id}", method = RequestMethod.GET, produces = "application/json")
????@ApiOperation(value = "Retrieves given entity", response=BusinessEntityModel.class)
????public?ResponseEntity> get(@Valid?@PathVariable?Long id){
????????checkIfExist(id);
????????return?new?ResponseEntity<> (businessEntityRepository.findByIdAndStatus(id, Status.PUBLISHED.ordinal()), HttpStatus.OK);
????}
@Transactional
????@RequestMapping(value = "/businessEntity", method = RequestMethod.POST, produces = "application/json")
????@ApiOperation(value = "Creates a new entity", notes="The newly created entity Id will be sent in the location response header")
????public?ResponseEntity create(@Valid?@RequestBody?BusinessEntityModel newEntity){
????????return?new?ResponseEntity <>(null, getHttpHeaders(businessEntityRepository.save(newEntity)), HttpStatus.CREATED);
????}
... 以編程方式管理事務(wù),我們必須在每個(gè)事務(wù)操作中包含事務(wù)管理代碼(樣板代碼)。結(jié)果,樣板事務(wù)代碼在這些操作中的每一個(gè)中重復(fù)。在大多數(shù)情況下,聲明式事務(wù)管理比程序化事務(wù)更可取。它是通過聲明將事務(wù)管理代碼與我們的業(yè)務(wù)方法分離來實(shí)現(xiàn)的。這可以幫助我們更輕松地為我們的應(yīng)用程序啟用事務(wù)并定義一致的事務(wù)策略,盡管聲明式事務(wù)管理不如程序化事務(wù)管理靈活。程序化事務(wù)管理允許我們通過代碼控制事務(wù)。
在精心設(shè)計(jì)的系統(tǒng)中使用的另一個(gè)有用的注解是@Autowired.@Autowired可以在構(gòu)造方法中使用來解析協(xié)作 bean 并將其注入到 bean 中,從而引導(dǎo)我們更好地設(shè)計(jì)應(yīng)用程序。使用接口與實(shí)現(xiàn)分離的原則和依賴注入模式開發(fā)的應(yīng)用程序易于測試,無論是單元測試還是集成測試,因?yàn)樵撛瓌t和模式可以減少我們應(yīng)用程序不同單元之間的耦合。
參數(shù) Java @Annotations
除了身份驗(yàn)證和授權(quán)之外,構(gòu)建安全 Web 服務(wù)的一個(gè)重要領(lǐng)域是確保輸入始終得到驗(yàn)證。Java Bean 注解提供了實(shí)現(xiàn)輸入驗(yàn)證的機(jī)制。我們可以通過@Valid在方法參數(shù)中使用注解來實(shí)現(xiàn)。
我們的類應(yīng)該在處理軟刪除之前驗(yàn)證傳入的標(biāo)識(shí)符請求。通過簡單地將@Valid注解添加到方法中,Spring 將確保傳入的標(biāo)識(shí)符請求首先通過我們定義的驗(yàn)證規(guī)則運(yùn)行。
...
??@Transactional
????@RequestMapping(value = "/businessEntity/{id}", method = RequestMethod.DELETE, produces = "application/json")
????@ApiOperation(value = "Deletes given entity")
????public?ResponseEntity delete(@Valid?@PathVariable?Long id, @Valid?@RequestBody?String?lastModifiedBy){
????????Optional softDelete = businessEntityRepository.findByIdAndStatus(id, Status.PUBLISHED.ordinal());
????????if?(softDelete.isPresent()) {
????????????BusinessEntityModel afterIsPresent = softDelete.get();
????????????afterIsPresent.setStatus(Status.UNPUBLISHED.ordinal());
????????????afterIsPresent.setLastModifiedBy(lastModifiedBy);
????????????businessEntityRepository.save(afterIsPresent);
????????????return?new?ResponseEntity<>(HttpStatus.OK);
????????}
????????else
????????????throw?new?ResourceNotFoundException("Entity with id "?+ id + " not found");
????}
... @PathVariable, 以及@RequestParam, 用于從 HTTP 請求中提取值,它們之間存在細(xì)微差別。@RequestParam用于從 URL ( https://www.jeevora.com/...?id=1) 獲取請求參數(shù),也稱為查詢參數(shù),同時(shí)@PathVariable從 URI ( ) 中提取值,https://www.jeevora.com/id/1如我們的案例研究所示。
@RequestBodyannotation 表示方法參數(shù)應(yīng)該綁定到 Web 請求的正文,而@ResponseBody表示方法返回值應(yīng)該綁定到 Web 響應(yīng)正文。
7
一個(gè)典型的設(shè)計(jì)錯(cuò)誤是將不同類型的邏輯(例如表示邏輯、業(yè)務(wù)邏輯和數(shù)據(jù)訪問邏輯)混合在一個(gè)大模塊中。由于它引入了緊密耦合,這降低了模塊的可重用性和可維護(hù)性。數(shù)據(jù)訪問對象 (DAO) 模式的一般目的是通過將數(shù)據(jù)訪問邏輯與業(yè)務(wù)邏輯和表示邏輯分開來避免這些問題。此模式建議將數(shù)據(jù)訪問邏輯封裝在稱為數(shù)據(jù)訪問對象 [3] 的獨(dú)立模塊中。
存儲(chǔ)庫或數(shù)據(jù)訪問對象 (DAO) 提供與數(shù)據(jù)存儲(chǔ)交互的抽象。存儲(chǔ)庫傳統(tǒng)上包括一個(gè)接口,該接口提供一組查找器方法,例如findById,findAll用于檢索數(shù)據(jù),以及持久化和刪除數(shù)據(jù)的方法。存儲(chǔ)庫還包括一個(gè)使用數(shù)據(jù)存儲(chǔ)特定技術(shù)實(shí)現(xiàn)此接口的類。習(xí)慣上每個(gè)域?qū)ο笥幸粋€(gè)存儲(chǔ)庫。盡管這是一種流行的方法,但在每個(gè)存儲(chǔ)庫實(shí)現(xiàn)中都有大量的樣板代碼重復(fù)。
package?com.BusinessEntityManagementSystem.dataAccessObject;
import?org.springframework.data.domain.Page;
import?org.springframework.data.domain.Pageable;
import?org.springframework.data.repository.NoRepositoryBean;
import?org.springframework.data.repository.PagingAndSortingRepository;
import?java.util.Optional;
@NoRepositoryBean
public interface IGenericRepository extends PagingAndSortingRepository {
????Optional<T> findByIdAndStatus(long?id, int?status);
????Page<T> findAllByStatus(int?status, Pageable?pageable);
} 使用@NoRepositoryBean注解,我們可以使用它來排除存儲(chǔ)庫接口被拾取,從而獲得一個(gè)正在創(chuàng)建的實(shí)例。這通常用于為所有存儲(chǔ)庫提供擴(kuò)展基接口并結(jié)合自定義存儲(chǔ)庫基類來實(shí)現(xiàn)在該中間接口中聲明的方法。在這種情況下,我們通常從中間接口派生出具體的存儲(chǔ)庫接口,但我們不想為中間接口創(chuàng)建 Spring bean。
參考
[1] Balaji Varanasi, Sudha Belida, Spring REST - Rest and Web Services development using Spring, 2015;
[2] Ludovic Dewailly,使用 Spring 構(gòu)建 RESTful Web 服務(wù) - 使用 Spring 框架構(gòu)建企業(yè)級(jí)、可擴(kuò)展的 RESTful Web 服務(wù)的動(dòng)手指南,2015;
[3] Marten Deinum, Daniel Rubio, Josh Long, Spring 5 Recipes: A Problem-Solution Approach, 2017。
