天天看點

用Java 17的Records加速Spring Boot開發

作者:愛科學的衛斯理

在《Spring Boot 2.6新特性:使用Java 17的Record作為配置屬性》,我們提到了使用Java Records來作為Spring Boot的配置屬性(configuration properties),進而減少了大量樣闆代碼的編寫,我們本篇将進一步拓展Records在Spring Boot下的應用場景,進而進一步減少我們的樣闆代碼,使代碼看上去更簡潔清晰。

用Java 17的Records加速Spring Boot開發

1、什麼是Records

record是一種特殊類型的類聲明,目的是為了減少樣闆代碼。record引入的主要目的是快速建立資料載體類。

這種類的主要目的就是在不同的子產品或者層之間包含并傳遞資料,它們表現為POJO(Plain Old Java Objects)和DTO(Data Transfer Objects)。

record聲明有專門的的關鍵字record,我們比較下一個簡單的POJO類和record上文法的差別:

POJO類:

@Data
public class Point {
    private String x;
    private String y;
}           

record:

public record Point(String x, String y) {
}           

我們建立一個簡單的示範項目,依賴如圖所示:

用Java 17的Records加速Spring Boot開發

2、使用record替代普通DTO

我們在Spring MVC的控制器中可以用一個record的DTO來接受前端傳遞來的資料:

@RestController
@RequestMapping("/people")
public class PersonController {

    private final PersonService personService;

    public PersonController(PersonService personService) {
        this.personService = personService;
    }

    @PostMapping
    public ResponseEntity<Person> save(@RequestBody PersonDto personDto){
        return ResponseEntity.ok(personService.save(personDto));
    }

    @GetMapping("/findByLastName")
    public ResponseEntity<List<PersonOnlyWithName>> findByLastName(String lastName){
        return ResponseEntity.ok(personService.findByLastName(lastName));
    }
}
           

上面的PersonDto是一個record:

public record PersonDto(String firstName, String lastName,Integer age) {
}           

3、使用record作為Spring的Bean

上面注入的PersonService,是一個Spring的Bean,它同樣可以是一個record,我們隻需要在record的參數裡寫上要被注入的bean,這個bean就會自動被注入:

@Service
public record PersonService(PersonRepository personRepository) {

  //儲存person
    public Person save(PersonDto personDto){
        Person person = new Person(personDto.firstName(), personDto.lastName(), personDto.age());
        return personRepository.save(person);
    }
	
  //按照lastName查詢people,傳回值隻有firstName和lastName
    public List<PersonOnlyWithName> findByLastName(String lastName){
        return personRepository.findByLastName(lastName);
    }
}           

在這裡的PersonRepository的bean可以自動被注入,代碼上比屬性@Autowired注入,甚至構造器注入代碼更簡潔。

Spring Data JPA用作資料通路的Repository:

public interface PersonRepository extends JpaRepository<Person, Long> {

    List<PersonOnlyWithName> findByLastName(String lastName);
}           

使用record來聲明bean,有一些潛在的問題:

1、record中,被注入的對象在目前對象裡其實是有一個隐藏的get方法:“personService.personRepository()”,這違反了資訊隐藏的封裝原則。

2、record定義了equals和hasCode方法,作為service并不需要。

3、service的變量屬性一般都是final。

如果上述的東西對你并沒有什麼影響,你可以自由決定是否使用。

3、使用record作為Spring Data JPA的projection

Spring Data JPA的projection目的是定制查詢的資料傳回,而不是傳回整個實體。一般情況下都是使用接口或者dto類,現在支援使用record。

定制的傳回的record内容為:

public record PersonOnlyWithName(String firstName, String lastName) {
}           

即我們查詢傳回的結果,不需要id和age,隻需要firstName和lastName。

4、示範應用

啟動程式,儲存Person,插入兩條資料:

用Java 17的Records加速Spring Boot開發

按照lastName查詢,檢視我們projection的效果:

用record改造Controller控制器

在上面我們的Controller用的還是普通的class,既然record可以聲明為bean并注入bean,那我們改造一下上面的Controller。

@RestController
@RequestMapping("/people")
public record PersonController(PersonService personService) {

    @PostMapping
    public ResponseEntity<Person> save(@RequestBody PersonDto personDto){
        return ResponseEntity.ok(personService.save(personDto));
    }

    @GetMapping("/findByLastName")
    public ResponseEntity<List<PersonOnlyWithName>> findByLastName(String lastName){
        return ResponseEntity.ok(personService.findByLastName(lastName));
    }
}           

代碼比構造器注入更精簡。

感謝對我的書《從企業級開發到雲原生微服務:Spring Boot實戰》的支援。

轉載請注明出處:今日頭條:愛科學的衛斯理

(此處已添加書籍卡片,請到今日頭條用戶端檢視)

繼續閱讀