Spring Annotation筆記整理

Posted by Kubeguts on 2020-03-10

Spring Annotation 筆記

這邊主要整理了我使用或是遇到的Spring Annotation資料查詢與統整的紀錄

API 相關 @Annotation

@RestController

將class設置為外部控制器

@RestControllerAdvice

等同於@ControllerAdvice + @ResponseBody

@ControllerAdvice為可在宣告的函式中使用@ExceptionHandler, @InitBinder或 @ModelAttribute註解的方法

@ExceptionHandler

攔截所有控制器所發出的exception,並返回body型式

@RequestMapping

控制器裡面的方法,使之成為外部請求

若要設置GET method, 如下
(也可以改成 POST, PUT, DELETE)

定義一組restful api前綴路由
ex : @RequestMapping("/api/v1")

1
2
3
4
5
6
7
@RequstMapping("/api/v1")
public class hello {
@GetMapping("/hello")
public hello(){
return "helloworld";
}
}

要使用的話得在postman中用 /api/v1/hello

HTTP動作Annotation

@PostMapping

@GetMapping

@PutMapping

@DeleteMapping

@RequestBody

可以接收Body參數,得透過class宣告一組object來接收

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Car {
private String type;
private Integer number;

public setType(String type) {
this.type = type;
}

public setNumber(Integer number) {
this.number = number;
}

public getType...
public getNumber...
}
1
2
3
4
5
6
7
@RequestMapping("/api/v1")
public class CarController{

// 透過宣告一個car,來接收Body的值 (type, number)
@GetMapping("/car")
function getCar(@RequestBody Car car);
}

@RequestHeader、@CookieValue

處理request header部分的注解:

Request Header的內容

1
2
3
4
5
6
Host                    localhost:8080  
Accept text/html,application/xhtml+xml,application/xml;q=0.9
Accept-Language fr,en-gb;q=0.7,en;q=0.3
Accept-Encoding gzip,deflate
Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive 300

透過 @RequestHeader("")接受其Header值

1
2
3
4
@RequestMapping("/displayHeaderInfo.do")  
public void displayHeaderInfo(@RequestHeader("Accept-Encoding") String encoding,
@RequestHeader("Keep-Alive") long keepAlive) {
}

@SessionAttributes

該註解用來綁定HttpSession中的attribute對象的值,便於在方法中的參數里使用。

該註解有value、types兩個屬性,可以通過名字和類型指定要使用的attribute 對象;

1
2
3
4
5
6
@Controller  
@RequestMapping("/editPet.do")
@SessionAttributes("pet")
public class EditPetForm {
// ...
}

參考資料:
Spring MVC常用注解@PathVariable、@RequestHeader、@CookieValue、@RequestParam、@RequestBody、@SessionAttributes、@ModelAttribute

https://www.cnblogs.com/EasonJim/p/8323017.html

@JsonProperty (重點,遇到接不到request的值,用這就對了)

用來接收application/json中,有大小不一型別的變數

ex: Postman script

1
2
3
4
5
6
7
{
"contact":{
"cPhone":"0912345678",
"cMail":" abc@test.com.tw ",
"cAddress":"台北市中正區信義路一段21-3號"
},...
}

若要接到 PHONE或Website,需要定義 @JsonProperty(“PHONE”), @JsonProperty(“Website”)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Data
public class Lia201iReqDTO {

contactDto contact;

@Data
@AllArgsConstructor
@NoArgsConstructor
public static class contactDto {
@NotNull
@JsonProperty("cPhone")
String cPhone;
@JsonProperty("cMail")
String cMail;
@JsonProperty("cAddress")
String cAddress;
}
}

參考資料

Spring REST consuming JSON uppercase vs lowercase
https://stackoverflow.com/questions/26890398/spring-rest-consuming-json-uppercase-vs-lowercase

驗證相關的Annotation

@Valid與@Validated

負責驗證有被定義校正的參數或object

先產生要校正的實體類(class)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Foo {    
@NotBlank
private String name;

@Min(18)
private Integer age;

@Pattern(regexp = "^1(3|4|5|7|8)\\d{9}$",message = "手机号码格式错误")
@NotBlank(message = "手机号码不能为空")
private String phone;

@Email(message = "邮箱格式错误")
private String email;

//... getter setter

}

於@Controller中 使用Valid做校正

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Controller
public class FooController {

// 對Foo class產生的foo做校正
@RequestMapping("/foo")
public String foo(@Validated Foo foo <1>, BindingResult bindingResult <2>) {
if(bindingResult.hasErrors()){
for (FieldError fieldError : bindingResult.getFieldErrors()) {
//...
}
return "fail";
}
return "success";
}

}

@Validate 與 @Valid差異

@Valid能進行嵌套驗證, @Validate不行

因為@Valid能加在成員屬性上,反而@Validated不能用在成員屬性。

舉例:

1
2
3
4
5
6
7
8
9
10
public class Item {

@NotNull(message = "id不能為空值")
@Min(value = 1, message = "id必須為正整數")
private Long id;

@NotNull(message = "props不能為空直")
@Size(min = 1, message = "至少要有一個屬性")
private List<Prop> props;
}

然而 Prop class如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Prop {

@NotNull(message = "pid不能為空")
@Min(value = 1, message = "pid必須為正整數")
private Long pid;

@NotNull(message = "vid不能為空")
@Min(value = 1, message = "vid必須為正整數")
private Long vid;

@NotBlank(message = "pidName不能為空")
private String pidName;

@NotBlank(message = "vidName不能為空")
private String vidName;
}

現在我們有個ItemController接受一個Item的參數,想要對Item進行驗證,如下所示:

1
2
3
4
5
6
7
8
@RestController
public class ItemController {

@RequestMapping("/item/add")
public void addItem(@Validated Item item, BindingResult bindingResult) {
doSomething();
}
}

在上圖中,如果Item實體的props屬性不額外加註釋,只有@NotNull和@Size,無論入參採用@Validated還是@Valid驗證,Spring Validation框架只會對Item的id和props做非空和數量驗證,不會對props字段裡的Prop實體進行字段驗證,也就是@Validated和@Valid加在方法參數前,都不會自動對參數進行嵌套驗證</ span>

:::info
這邊指的嵌套(Nested)驗證,指的是若class Item中還有一個變數List 且這個Props的class內也有自己定義驗證@Notnull, @NotBlank, 一層class內還有一個class定義自己的驗證,就叫做嵌套驗證
:::

要進行嵌套驗證的話得在 class Props中加入@Valid

1
2
3
4
5
6
7
8
9
10
11
public class Item {

@NotNull(message = "id不能为空")
@Min(value = 1, message = "id必须为正整数")
private Long id;

@Valid // 嵌套验证必须用@Valid
@NotNull(message = "props不能为空")
@Size(min = 1, message = "props至少要有一个自定义属性")
private List<Prop> props;
}

補充 負責校驗的Annotation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Null 被註釋的元素必須為 null
@NotNull 被註釋的元素必須不為 null
@AssertTrue 被註釋的元素必須為 true
@AssertFalse 被註釋的元素必須為 false
@Min(value) 被註釋的元素必須是一個數字,其值必須大於等於指定的最小值
@Max(value) 被註釋的元素必須是一個數字,其值必須小於等於指定的最大值
@DecimalMin(value) 被註釋的元素必須是一個數字,其值必須大於等於指定的最小值
@DecimalMax(value) 被註釋的元素必須是一個數字,其值必須小於等於指定的最大值
@Size(max=, min=) 被註釋的元素的大小必須在指定的範圍內
@Digits (integer, fraction) 被註釋的元素必須是一個數字,其值必須在可接受的範圍內
@Past 被註釋的元素必須是一個過去的日期
@Future 被註釋的元素必須是一個將來的日期
@Pattern(regex=,flag=) 被註釋的元素必須符合指定的正則表達式

@NotBlank(message =) 驗證字符串非null,且長度必須大於0
@Email 被註釋的元素必須是電子郵箱地址
@Length(min=,max=) 被註釋的字符串的大小必須在指定的範圍內
@NotEmpty 被註釋的字符串的必須非空
@Range(min=,max=,message=) 被註釋的元素必須在合適的範圍內

補充: 用BindingResult接收@Valid拋出來的錯誤訊息

假設有驗證錯誤的物件為 BindingResult bindingResult
可透過bindResult取得錯誤資訊

舉例:

1
2
3
4
5
6
7
8
9
public class FlightDTO {
@NotBlank
private String terminal; // 航廈
@NotBlank
private String type; // 航班種類
@NotBlank
private String carrier; // 航空公司代碼
...
}

若使用@Valid對FlightDTO做參數驗證,假設有一Request少輸入terminal資訊,就會發出bindingResult訊息,其中包含:

1
2
3
4
5
6
7
8
9
10
List<FieldError> fieldErrors = bindingResult.getFieldErrors();
for(FieldError fieldError: fieldErrors) {
// 封裝錯誤格式
FieldResource fieldResource = new FieldResource(
fieldError.getObjectName(), // 取得物件名稱:為FlightDTO
fieldError.getField(), // 為properties(terminal)
fieldError.getCode(), // 為@Notblank
fieldError.getDefaultMessage() // 為@Notblank的錯誤資訊
);
}

:::info
BindingResult為Error的子介面

BindingResult
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/validation/BindingResult.html
Error:
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/validation/Errors.html
:::

參考資料:

使用spring validation完成数据后端校验
https://www.cnkirito.moe/spring-validation/

@Validated和@Valid区别:Spring validation验证框架对入参实体进行嵌套验证必须在相应属性(字段)加上@Valid而不是@Validated
https://blog.csdn.net/qq_27680317/article/details/79970590

操作數據庫相關數據相關的Annotation

@Entity

表示是一個對應到DB Table的物件

@Id

聲明該欄位為主鍵

@GeneratedValue

指定ID生成的策略,若沒有設置的化, Hibernate對應的是為一個Not null的物件,得自行設定id到物件內。
但通常會自行在新增資料時產生ID, 故@Id與@GeneratedValue會常常同時出現

因為Hibernate會遇見不同的Database,所以有各種不同的生成策略:

GenerationType.Auto

Hibernate會根據採用的Database為何,來決定要用 GenerationType.Identity 或 GenerationType.SEQUENCE。
(大部分的情況都會採用 GenerationType.SEQUENCE)

GenerationType.IDENTITY

透過auto-incremented database column來產生primary key.
常見的MySQL和MMSQL都採用該方式,相對應的DDL語言為以下所示:

1
id BIGINT NOT NULL AUTO_INCREMENT

GenerationType.SEQUENCE

設定該策略,通常也會給入另一個Annotation @SequenceGenerator,如下

1
2
3
4
5
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "author_generator")
@SequenceGenerator(name="author_generator", sequenceName = "author_seq", allocationSize=50)
@Column(name = "id", updatable = false, nullable = false)
private Long id

若沒有指定,Hibernate會使用預設的SequenceGenerator

可以看到若指定 sequenceGenerator的name為author_seq, Hibernate就會在 author_seq這個table查找下一個id值為多少

注意,在Postgres情況下不適合用該方法
https://stackoverflow.com/questions/4288740/hibernate-use-of-postgresql-sequence-does-not-affect-sequence-table/4502062#4502062

GenerationType.SEQUENCE

當不希望應用程式與某一種 Database Engine 綁死的時候,可以使用這種方法,透過另外一個表格來定義 ID

會透過SQL,建立存放各種column的seqeunce

1
CREATE TABLE APP_SEQ_STORE ( APP_SEQ_NAME VARCHAR(255) NOT NULL, APP_SEQ_VALUE BIGINT NOT NULL, PRIMARY KEY(APP_SEQ_NAME) );
Reference: https://thoughts-on-java.org/jpa-generate-primary-keys/

@IdClass 复合主键

复合主键由多个主键字段组成。每个主键字段必须是上面列出的支持类型之一。
例如,以下项目实体类的主键由两个字段组成:

1
2
3
4
5
@Entity @IdClass(ProjectId.class)
public class Project {
@Id int departmentId;
@Id long projectId;
}

在ProjectId.class中聲明兩個主鍵值

1
2
3
4
Class ProjectId {
int departmentId;
long projectId;
}

@Embeddable

嵌入式主键
表示复合主键的另一种方法是使用可嵌入的类

1
2
3
4
5
6
7
8
9
10
@Entity
public class Project {
@EmbeddedId ProjectId id;
}

@Embeddable
Class ProjectId {
int departmentId;
long projectId;
}

@Column

宣告該變數與資料庫欄位的對映

1
2
3
4
@Column(nam=”category_name” length=20)
Public void getCategoryName(){
Return this.categoryName;
}

參考資料:

SPRING中常用的注解(@ENTITY,@TABLE,@COLUMN,@REPOSITORY,@SERVICE)https://www.cnblogs.com/hoojjack/p/6568920.html

JPA 主键@Id、@IdClass、@Embeddable、@EmbeddedId
https://blog.csdn.net/tracycater/article/details/78319021

@Data

@Data 自動產生 getter(), setter(), toString(), 節省一大堆代碼的神Annotation

前置作業: 引入lombok

要使用 @Data 注解要先引入lombok

1
lombok為一個Library,可以用簡單的註解形式來簡化代碼,提高開發效率。

如何使用lobok

  1. maven中添加依賴
1
2
3
4
5
6
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
<scope>provided</scope>
</dependency>
  1. 在編譯器中添加插件

以IDEA為例,在setting的plugin裡搜索lombok plugin,安裝插件。

直接在Class上加上@Data即可

例如一个简单的Person Class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
public class Person {
private String name;
private String address;
private Integer age;
private String hobbit;
private String phone;

public Person() {
}

public Person(String name, String address, Integer age, String hobbit, String phone) {
this.name = name;
this.address = address;
this.age = age;
this.hobbit = hobbit;
this.phone = phone;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getAddress() {
return address;
}

public void setAddress(String address) {
this.address = address;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

public String getHobbit() {
return hobbit;
}

public void setHobbit(String hobbit) {
this.hobbit = hobbit;
}

public String getPhone() {
return phone;
}

public void setPhone(String phone) {
this.phone = phone;
}

@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
", age=" + age +
", hobbit='" + hobbit + '\'' +
", phone='" + phone + '\'' +
'}';
}
}

使用了 @Data

1
2
3
4
5
6
7
8
9
10
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private String name;
private String address;
private Integer age;
private String hobbit;
private String phone;
}

在按快捷鍵 Ctrl + F12,可以查找到set,get,toString 方法。

使用 @Data 註解就可以有下面幾個註解的功能: @ToString、@Getter、@Setter、@EqualsAndHashCode、@NoArgsConstructor 。

:::info
常用的幾個註解:
@Data : 注在類上,提供類的get、set、equals、hashCode、canEqual、toString方法
@Setter : 注在屬性上,提供 set 方法
@Getter : 注在屬性上,提供 get 方法
構造函數

@AllArgsConstructor
会生成一个包含所有变量,同时如果变量使用了NotNull annotation , 会进行是否为空的校验,
全部参数的Constructor函数的自动生成,该注解的作用域也是只有在实体类上,参数的顺序与属性定义的顺序一致。

範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import lombok.AllArgsConstructor;

@AllArgsConstructor
class MyClass{
private String one;
private Integer three;
private Integer two;
}

自動生成建構子:
public MyClass(String one, Integer three, Integer two) {
this.one = one;
this.three = three;
this.two = two;
}

參考: https://stackoverflow.com/questions/49106072/how-to-specify-the-order-of-parameters-in-allargsconstructor-in-lombok

@NoArgsConstructor
產生沒有參數的建構子
generate a constructor with no parameters.

@RequiredArgsConstructor
会生成一个包含常量(final),和标识了@NotNull的变量 的构造方法。

@EqualsAndHashCode : 注在类上,提供对应的 equals 和 hashCode 方法
@Log4j/@Slf4j : 注在类上,提供对应的 Logger 对象,变量名为 log
:::

:::warning
注意的是,同时使用@Data 和 @AllArgsConstructor 后 ,默认的无参构造函数失效,如果需要它,要重新设置 @NoArgsConstructor
:::

參考資料

@Data注解 与 lombok
https://www.jianshu.com/p/c1ee7e4247bf
学习Spring Boot:(十五)使用Lombok来优雅的编码
https://blog.wuwii.com/springboot-15.html

Spring載入Bean與標示為Bean有關的Annotation

@Bean

定義可共物件化的類別,叫做Bean,並且放置在Spring Ioc Container中並且給其管理,等待被呼叫使用。

配置方式

1. 程式碼Java Code配置

Spring 的@Bean通常宣告在掛有@Configuration的Spring配置類別中的方法前,例如宣告一個 AppConfig.java,若有超過一個以上的bean,使用 @Qualifier給予名稱

AppConfig.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Configuration
public class AppConfig {
@Bean(name="add")
Calculator addCalculator() {
return new AddCalculator();
}

@Bean(name="sub")
Calculator subCalculator() {
return new SubCalculator();
}

@Bean(name="mul")
Calculator mulCalculator() {
return new MulCalculator();
}
}

定義Calculator的介面

Calculator.interface

1
2
3
public interface Calculator {
int calc(int a, int b);
}

定義一個實作Calculator的類別

CalculatorImpl.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.will.advanced.demo.bean;

class AddCalculator implements Calculator {

@Override
public int calc(int a, int b) {
return a + b ;
}
}

class SubCalculator implements Calculator {
@Override
public int calc(int a, int b) {
return a - b;
}
}

class MulCalculator implements Calculator {
@Override
public int calc(int a, int b) {
return a * b;
}
}

取得註冊的bean的方式

1. 使用@Autowire 與 @Qualifier

然後在 CommandLineRunner中使用 @AutoWire使用@Bean
,然而若要Autowired的類別對象是介面的話,且介面有很多的實作子類別
(AddCalculator, SubCalculator, MulCalculator)

那得透過 @Qualifier來指名是哪一個子類別

CommandLineAppStartupRunner.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@SpringBootApplication
public class CommandLineAppStartupRunner implements CommandLineRunner {
@Autowired
private ApplicationContext context;
@Autowired
@Qualifier("add")
Calculator addCalculator;
@Autowired
@Qualifier("sub")
Calculator subCalculator;
@Autowired
@Qualifier("mul")
Calculator mulCaculator;

@Override
public void run(String...args) throws Exception {
System.out.println(addCalculator.calc(1,3));
System.out.println(subCalculator.calc(3,2));
System.out.println(mulCaculator.calc(2,6));
}
}
2. 使用getBean()

CommandLineAppStartupRunner.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@SpringBootApplication
public class CommandLineAppStartupRunner implements CommandLineRunner {

@Autowired
private ApplicationContext context;

@Override
public void run(String...args) throws Exception {
// 取得所有已經註冊的bean
System.out.println(Arrays.asList(context.getBeanDefinitionNames()));
// 取得名稱為add的bean
Calculator addCalculator = context.getBean("add", Calculator.class);
Calculator subCalculator = context.getBean("sub", Calculator.class);
Calculator mulCalculator = context.getBean("mul", Calculator.class);

System.out.println(addCalculator.calc(1,3));
System.out.println(subCalculator.calc(3,2));
System.out.println(mulCalculator.calc(2,3));
}
}
補充: 使用CommandlineRunner讓Spring設置可以先被載入,然後可透過run()定義自訂義執行的內容

https://dzone.com/articles/spring-boot-applicationrunner-and-commandlinerunne

3. xml檔案配置

上面的寫法等同在 appConfig.xml 宣告:

1
2
3
<beans>
<bean name="addCalculator" class="com.will.advanced.TransferServiceImpl"/>
</beans>

@ComponentScan: 掃描指定package中掛有@Component的類別,自動註冊包含的bean

不過一般在沒有特殊需求的情況下都會使用@ComponentScan掃描指定package中掛有@Component的類別來自動註冊為bean。

而會使用@Bean的時機為,當要被註冊為bean的類別建構步驟或邏輯比較複雜,此時就需要@Bean讓你可以在構造bean的方法內撰寫構造的詳細邏輯,而@ComponentScan就無法滿足比較細微的配置。

例如在 service層中新增一個 TestService class
透過@Component將TestService註冊成Bean

1
2
3
4
5
6
@Component
public class TestService {
public String test() {
return "testing";
}
}

透過@ComponentScan可使用@Component註冊的bean,

1
2
3
4
5
6
7
8
9
10
@ComponentScan
public class CommandLineAppStartupRunner implements CommandLineRunner {
@Autowired
TestService testService;

@Override
public void run(String...args) throws Exception {
System.out.println(testService.test());
}
}
參考資料

Spring: A Head Start 🔥 — Beans Configuration (Part 2)
https://medium.com/omarelgabrys-blog/spring-a-head-start-beans-configuration-part-2-4a8c239b070a

@Scope: 定義Bean被引用時要怎樣被使用

  • singleton:在Spring IoC Container,該bean只會有單一實例(a single instance),此為Spring預設值
  • prototype:在Spring IoC Container中,該bean可以有多個實例(any number of object instances)
  • request: 在每一次的HTTP Request,spring container會根據loginAction bean的定義來建立一個全新的instance,而且僅在目前的request中有效,所以可以放心的去更改instance的內部狀態,請求結束,request scope的bean instance會被destroy
  • session:針對某個HTTP Session,spring container會根據userPreference bean的定義來建立一個全新的instance,同樣的,和request scope一樣,可以放心的去更改instance內部狀態。
  • global-session:僅在portlet為基礎的Web應用下有作用。Porlet的規範中定義了global session的概念。
參考資料:

Spring Bean Scope 學習
https://kevingo75.blogspot.com/2012/03/spring-bean-scope.html

補充: 什麼是Spring IoC (Inversion of Control) container.

Inversion of Control, or IoC for short, is a process in which an object defines its dependencies without creating them. This object delegates the job of constructing such dependencies to an IoC container.

用一柱話描述IoC: class不用自己主動new欲依賴class的instance,而是透過外部 (main()方法) 以建構子、setter或是介面注入來產生依賴class的instance

:::success
需要的 遊戲,不用自己 下載,而是 網咖提供 給你。
----------------------||---------------------

需要的 物件,不用自己 取得,而是 服務容器 提供 給你。
----------------------||---------------------
需要的 依賴實例,不用 主動 (Active) 建立,而是 被動 (Passive) 接收。
:::

參考資料:

控制反轉 (IoC) 與 依賴注入 (DI)
https://notfalse.net/3/ioc-di

@Repository

简化 Spring 的开发。@Repository注解便属于最先引入的一批,它用于将数据访问层 (DAO 层 ) 的类标识为 Spring Bean。具体只需将该注解标注在 DAO类上即可。

@Service、@Controller 和 @Component 將class標示为Bean

  • @Component 是一个泛化的概念,仅仅表示一个组件 (Bean) ,可以作用在任何层次。
  • @Service 通常作用在業務邏輯層,但是目前该功能与 @Component 相同。
  • @Controller 通常作用在控制層(Controller),但是目前该功能與@Component 相同。

@PostConstruct

被@PostConstruct修飾的方法會在服務器加載Servlet的時候運行,並且只會被服務器執行一次。

@Transactional

假若你在class中設定了@Transactional, 該class所有的method有使用到JPA的都會被spring的transaction所管理。

舉例:當有個Transaction有三個動作:entity1.save, entity2.save, entity3.save.

若entity3.save失敗,那Spring就會將entity1.save與entity2.save的執行結果給rollback,還原其動作。

參考資料

https://stackoverflow.com/questions/1099025/spring-transactional-what-happens-in-background

AOP相關的Annotaion

@Aspect

@Aspect:定義AOP,作用在class

AOP為 Aspect Oritented Programming
AOP通過給程序定義一個切入點,然後在其前後切入不同的執行內容
AOP不會破壞原來的程序邏輯
AOP使用場景:

  • 紀錄日誌
  • 事務管理
  • 安全檢查
  • 資源控制

@Pointcut, @Before, @After

範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Aspect
@Component
public class LogAspect {

private final Logger logger = LoggerFactory.getLogger(this.getClass());
// 切入package為com.william.demo.api的LogTestApi類別的log函式
@Pointcut("execution(* com.william.demo.api.LogTestApi.log(..))")
public void log() {

}

// 希望在切入點 log()函式前執行某些內容
// 或是用直接用 @Before("execution(* com.william.demo.api.LogTestApi.log(..))");
@Before("log()")
public void doBefore() {
logger.info("----------do before---------");
}

@After("log()")
public void doAfter() {
logger.info("----------do after----------");
}
}

其他Annotation

@Cache

定義cache策略與範圍,可以定義以下參數:

  • usage: 當前緩存策略(NONE, READ_ONLY, NONSTRICT_READ_WRITE, TRANSACTIONAL)

    • read-only: 只讀緩存
      • 如果你的應用程序只需讀取一個持久化類的實例,而無需對其修改, 那麼就可以對其進行只讀緩存
    • read-write: 讀寫緩存
      • 如果應用程序需要更新數據,那麼使用讀/寫緩存比較合適。如果應用程序要求“序列化事務”的隔離級別(serializable transaction isolation level),那麼就決不能使用這種緩存策略
    • nonstrict-read-write: 不嚴格讀寫緩存
      • 如果應用程序只偶爾需要更新數據(也就是說,兩個事務同時更新同一記錄的情況很不常見),也不需要十分嚴格的事務隔離,那麼比較適合使用非嚴格讀/寫緩存策略。
    • transactional :事務性緩存
      • Hibernate 的事務緩存策略提供了全事務的緩存支持,例如對 JBoss TreeCache 的支持。這樣的緩存只能用於 JTA 環境中,你必須指定為其hibernate.transaction.manager_lookup_class屬性。
  • region: 可選參數,指定二級緩存的去域名,默認為類或者集合的名字。

  • include: 可選參數(all, non-lazy)。 all包含所有屬性,non-lazy僅包含非延遲加載的屬性。

範例

1
2
3
4
5
@Table(name = "PROVICE")
@Cache(usage =CacheConcurrencyStrategy.READ_ONLY)
public class Province() {
...
}
1
2
3
4
5
6
7
@Table(name = "PROVICE")
@Cache(usage =CacheConcurrencyStrategy.READ_ONLY)
public class Province() {
@OneToMany(fetch = FetchType.LAZY)
@JoinColumn(name = "cityId")
@Cache(usage = CacheConcrrencyStrategy.READ_ONLY)
private Set<City> cities;
參考資料

Hibernate @Cache注解-天才小小布
https://blog.csdn.net/w410589502/article/details/54603265

@Configuration

@Configuration的作用同以前的xml配置檔(例如Spring的applicationContext.xml或dispatcher-servlet.xml),用來設定Spring環境配置,例如宣告及註冊bean至Spring容器中,注入properties參數等。

範例

例如我們有一個FooService類別,若此類別要成為Spring容器管理的bean,有兩種方法:

  1. 一般在該類別上宣告@Component並搭配@ComponentScan掃描的方式註冊為bean
  2. 透過在@Configuration類別中搭配@Bean的方式註冊。

例如建立一個AppConfig並在類別名稱前掛上@Configuration,則此類別及成為Spring的配置類。在配置類的方法前掛上@Bean則方法回傳的物件就會被註冊為Spring容器管理的bean

fooService()方法便會將FooService註冊為bean。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.will.advanced.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.will.advanced.demo.service.FooService;

@Configuration
public class AppConfig {
@Bean
public FooService fooService() {
return new FooService();
}
}

為了程式管理及維護上的方便,通常我們會把某些相關的配置寫在另外的配置檔,而不是全部塞在同一個類別,此時就可以利用@Configuration另外定義一個配置檔,例如上面的AppConfig。

@SpringBootApplication: 本身也包含@Configuration

而在Spring Boot中,@SpringBootApplication類本身即包含了@Configuration,所以可以直接在裡面進行如上Bean的配置,例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.will.advanced.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import com.will.advanced.demo.FooService;

@SpringBootApplication
public class SpringBootBeanDemoApplication {

public static void main(String[] args) {
SpringApplication.run(SpringBootBeanDemoApplication.class, args);

}
@Bean
public FooService fooService() {
return new FooService();
}

}
參考資料

Spring @Configuration作用
https://matthung0807.blogspot.com/2019/04/spring-configuration_28.html

@SpringBootApplication

定義 @SpringBootApplication的預設掃瞄bean的範圍

Spring Boot的@SpringBootApplication預設只會掃描所屬package下的類,因此若@Configuration類別定義在@SpringBootApplication類以外的package,則要用scanBasePackages屬性來設定要額外掃描的package,例如@SpringBootApplication(scanBasePackages=“com.will.advanced.demo.config”) (@Configuration包含了@Component),

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.will.advanced.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
// 注意在scanBasePackages的package名稱
@SpringBootApplication(scanBasePackages="com.will.advanced.demo.config")
public class SpringBootBeanDemoApplication {

public static void main(String[] args) {
SpringApplication.run(SpringBootBeanDemoApplication.class, args);

}
}

@Import

或是透過@Import來引入 ,例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.will.advanced.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;

@SpringBootApplication
// 注意pacakge名稱
@Import(value = { com.will.advanced.demo.config.AnotherAppConfig.class })
public class SpringBootBeanDemoApplication {

public static void main(String[] args) {
SpringApplication.run(SpringBootBeanDemoApplication.class, args);

}
}

:::warning
注意使用@Import會忽略@Configuration設定
:::

@Autowired

依賴注入物件

Spring的@Autowired用來依賴注入物件,典型的用法就是掛在類別成員變數上。@Autowired預設會依注入對象的類別型態來選擇容器(透過@Configuration定義的class會成為容器)中相符的物件(設置成bean)來注入。

1
2
@Autowired
private Car car;

Spring會自動去容器中找到 有容器定義CarBeans的bean

1
2
3
4
5
6
import ....xxx.Car;
@Configuation
public class CarBeans {
@Bean
public Car car() { return new Car() }
}

@Autowire為Spring IoC的實現,不用自己去new一個car出來,而是透過容器(會做constructor or setter or interface injection),來去實現外部注入,降低模組(class)之間的耦合性

@EnableScheduling

若開發者在AppConfig類中使用了@EnableScheduling,並在某個task class中使用了@Schedule註解,那麼被@Schedule標註的方法既可以在指定時間內自動執行

注意:@EnableScheduling要與@Schedule搭配使用才有效果

@ConfigurationProperties

若想要把配置文件的信息,讀取好並自動封裝(getter讀取private的config值)成實體類,如此一來我們在代碼裡面使用就輕鬆許多,這時就可以使用@ConfiguationProperties,把同類的配置訊息自動封裝成實體類。

假設有設定文件在 application.properties之下:

1
2
3
connection.username=admin
connection.password=kyjufskifas2jsfs
connection.remoteAddress=192.168.1.1

我們可以定義一個實體類在裝載配置文件信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Component
@ConfigurationProperties(prefix="connection")
public class ConnectionSettings {

private String username;
private String remoteAddress;
private String password ;

public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getRemoteAddress() {
return remoteAddress;
}
public void setRemoteAddress(String remoteAddress) {
this.remoteAddress = remoteAddress;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}

}

亦可以把 @ConfigurationProperties 定義在@Bean之下,說明@Bean是參考到有用configuationProperties設定的class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@SpringBootApplication
public class DemoApplication{

//...

@Bean
@ConfigurationProperties(prefix = "connection")
public ConnectionSettings connectionSettings(){
return new ConnectionSettings();

}

public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}

需要使用時直接用@Autowire注入

1
2
3
4
5
6
7
8
9
10
11
12
13
@RestController
@RequestMapping("/task")
public class TaskController {
// 在這裡注入 config!
@Autowired ConnectionSettings conn;

@RequestMapping(value = {"/",""})
public String hellTask(){
String userName = conn.getUsername();
return "hello task !!";
}

}
參考資料:

spring boot 使用@ConfigurationProperties
https://blog.csdn.net/yingxiake/article/details/51263071

@ConditionalOnClass

是Springboot实现自动配置的重要支撑之一。其用途是判断当前classpath下是否存在指定类,若是则将当前的配置装载入spring容器。

參考資料:

@ConditionalOnClass的使用探索 新日暮里格斗大会
https://blog.csdn.net/lucyTheSlayer/article/details/80430912

@ConditionalOnMissingBean

结合使用注解@ConditionalOnMissingBean和@Bean,可以做到只有特定名称或者类型的Bean不存在于BeanFactory中时才创建某个Bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

@Configuration
public class ConditionalOnMissingBeanConfig {
// 無條件定義一個bean
@Bean
public A beanA (){
return new A(); // 
}

@Bean
 @ConditionalOnMissingBean(name="beanA")
 public B beanB(){
// 如果 beanFactory 中存在一个名称为 beanA 的 bean,才定义bean : beanB;
  // 因为上面的方法已经定义了一个 beanA,所以这里 beanB定义并不会发生。
return new B();
 }


 @Bean
 @ConditionalOnMissingBean(name="beanD")
 public C beanC(){
// 如果 beanFactory 中存在一个名称为 beanD的 bean,才定义bean : beanC;
return new C();
 }
}

參考資料:

Spring Boot基于特定条件创建Bean例子 : ConditionalOnMissingBeanhttps:
//blog.csdn.net/andy_zhang2007/article/details/81285130