Java — Spring Annotation

常用註解整理

Mask
4 min readMay 6, 2023

在閱讀網路上 Spring Boot 的專案時發現沒看過的註釋有點多,在不明白功用的情況下看得有點沒意思,先就常用的註釋理解一下

Photo by Amanda Jones on Unsplash

RequestMapping

將 http request 與處理函式對應,根據設定值可以選擇要處理的 http method 或各種指定值,可以用於類或函式

/*
* value/path -> url路徑
* method -> 接受的http method
* params -> 指定參數的值,匹配到才會進行處理
* heraders -> 指定header的值
* consumes -> 指定 Content-Type
* produces -> 指定 Accept
*/
@RequestMapping(value = "/home", method = RequestMethod.GET , consumes = "application/json")
public Map<String, String> hello() {
Map<String, String> response = new HashMap<>();
response.put("message", "Hello, World!");
return response;
}

@RequestMapping 可以根據設定決定處理的 http method,但在其介紹中表示,大多數情況偏向使用各自 http method 的註釋

GetMapping

處理 Get method,可用的配置參數與 RequestMapping 一樣

PostMapping

處理 Post method

PutMapping

處理 Put method

DeleteMapping

處理 Delete method

PatchMapping

處理 Patch method

Options 沒有自己的 Mapping 可以使用,需使用 @RequestMapping 並指定 Options Method
以上各自 http method 的 Annotation 都只能用於函式,只有 RuquestMapping 可以用於類上

RequestBody

將 Http Body 的參數解析並綁定到指定的對象,常用於處理 PostPut 的函式參數,只支持 Json 格式的資料

@PostMapping("/create/user")
public User hello(@RequestBody User user) {
return user;
}

ModelAttribute

根據使用方式,有綁定參數、優先執行的功能

使用於 Controller 的函式上

只要調用到 Controller 內的函式,都會先執行 @ModelAttribute 指定的函式

@Controller
@ResponseBody
public class HelloWorld {
Map<String, String> res = new HashMap<>();

@ModelAttribute("Default")
public void Default() {
res.put("messageDefault", "Hello");
}

@GetMapping("/hello")
public Map<String, String> hello() {
res.put("message", "World");
return res;
}
}

/*
{
"messageDefault": "Hello",
"message": "World"
}
*/

使用在函式參數上

可以將請求參數綁定到物件上,但此註解只支持 form-data 的資料,並且物件的構造函式需配合賦值

@Controller
@ResponseBody
public class HelloWorld {
Map<String, String> res = new HashMap<>();

@PostMapping(value = "/create/user")
public User Create(@ModelAttribute User user) {
return user;
}

}

class User {
public String name;
public int age;

public User(String name, int age) {
this.name = name;
this.age = age;
}
}

Component

是 Spring Framework 的核心註解,同時也是一個通用註解,標記此註解的類會實例化為 Bean,並由 Spring 容器所管理,使其他 Bean 可使用,許多註解都由此延伸而出

Controller

用於定義 Controller ,使用在類上,通常結合 View 返回資料,如果需要返回的純資料,則要額外使用 @ResponseBody

@Controller
@ResponseBody
public class HelloWorld {

@GetMapping("/hello")
public Map<String, String> hello() {
Map<String, String> res = new HashMap<>();
res.put("message", "HelloWorld");
return res;
}
}

RestController

@Controller@ResponseBody 的組合體,如果想要返回的是純資料,可以直接使用這個註解,在 Spring Boot 中會使用這個

@RestController
public class HelloWorld {

@GetMapping("/hello")
public Map<String, String> hello() {
Map<String, String> res = new HashMap<>();
res.put("message", "HelloWorld");
return res;
}
}

CrossOrigin

設置跨域請求的註解,使用此註解的路由可接受設定的跨域請求

@RestController
public class HelloWorld {
Map<String, String> res = new HashMap<>();

/*
* origins -> 允許跨域的URI
* methods -> 允許跨域請求的方法
* allowedHeaders -> 允許跨域請求的Header
* allowCredentials -> 是否允許發送認證資訊 bool
* maxAge -> 指定瀏覽器緩存時間
*/
@CrossOrigin(origins = "*", maxAge = 3600)
@PostMapping(value = "/create/user")
public User Create(@RequestBody User user) {
return user;
}

}

InitBinder

主要用法是在 Controller 接受到請求時,在函式進行處理前,創建一個自定義的 Binder 對象,並設定給屬性編輯器 PropertyEditor

根據 http 請求的資料格式不同,觸發 InitBinder 時機有所不同

Json

application/json 來說,在接取資料時會使用 @RequestBody ,而該註解會先進行資料綁定,而後 Spring 才會使用 Binder 進行資料轉換,以下例來說,即便在 InitBinder 指定了 yy-MM-dd 為日期格式,也會因為先進行 Date資料綁定,而進行綁定的 HtppMessageConverter 無法辨認該日期格式而報錯

@RestController
public class HelloWorld {
Map<String, String> res = new HashMap<>();

@InitBinder
public void initBinder(WebDataBinder binder) {
System.out.println("Heloow");
SimpleDateFormat dateFormat = new SimpleDateFormat("yy-MM-dd");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat,
false));
}

@PostMapping(value = "/create/user")
public User Create(@RequestBody User user) {
return user;
}
}

class User {
public String name;
public int age;

SimpleDateFormat dateFormat = new SimpleDateFormat("yy-MM-dd");
public Date birthday;

public User(String name, int age, Date birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;

}
}

以此例來說,要完成日期格式辨認,只需將 User 的建構函式改成接 String birthday 即可,讓 HttpMessageConvert 不要進行 Date 的資料轉換,就不會報錯

form-data

multipart/form-data 的請求會使用 @ModelAttribute 進行資料綁定,該註解會使用 Binder 進行資料綁定,其中就會包含資料轉換的步驟,所以會在資料綁定前調用 InitBinder,就可以完成對 yy-MM-dd的辨認,所以上述的例子在使用 form-data 傳入資料是可以正確完成的

@RestController
public class HelloWorld {
Map<String, String> res = new HashMap<>();

@InitBinder
public void initBinder(WebDataBinder binder) {
System.out.println("Heloow");
SimpleDateFormat dateFormat = new SimpleDateFormat("yy-MM-dd");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat,
false));
}

@CrossOrigin(origins = "*", maxAge = 3600)
@PostMapping(value = "/create/user", consumes = "multipart/form-data")
public User Create(@ModelAttribute User user) {
return user;
}
}

class User {
public String name;
public int age;
public Date birthday;

public User(String name, int age, Date birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;
}
}

Binder 是為綁定器,將請求綁定到指定的對象上, @InitBinder 的用途就是在進行綁定前對 Binder 進行定義,達到將特定參數轉為指定格式或驗證等功能

Service

@Component 的延伸,用於標注業務邏輯類,會被 Spring 實例為 Bean 並被容器管理

import org.springframework.stereotype.Service;

@Service
public class UserService {
public void createUser(String name, int age) {
// do something
}
}

Repository

Component 的延伸,用於標注資料持久化層

import org.springframework.stereotype.Repository;

@Repository
public class UserRepository {
public void create(String name, int age) {
// do something
}
}

ConditionalOnBean

當某 Bean 存在於容器中時,才實例被標注的類

@Bean
public class Foo {}

@Bean
@ConditionalOnBean(Foo.class)
public class Bar {
// ...
}

ConditionalOnMissingBean

當某 Bean 不存在時,被標注的類將被實例

@Bean
public class Foo {}

@Bean
@ConditionalOnMissingBean(Foo.class)
public class Bar {
// ...
}

ConditionalOnClass / ConditionalOnMissingClass

與上述 Bean 相似,但是基於類,主要是用於控制依賴項在 classpath 中才進行配置與否

ConditionalExpression

一個條件表達式,可以在啟動時動態的選擇是否注入某個 Bean 或啟用配置

  • Classpath 是否存在某個類
  • Classpath 是否存在某個 Resource
  • 系統屬性是否存在
  • 環境變量是否存在
  • 某個 Bean 是否已注入
  • 當前應用程序是否運行在指定的 Web 應用程序環境中
@Bean
@ConditionalOnExpression("${spring.datasource.enabled:true}")
public DataSource dataSource() {
// 配置數據源
}

Conditional

用來根據一定的條件決定被標注的類是否要實例化,傳入的類需要實現 Condition 並實作 matches ,根據回傳決定被標注的類實例與否

@Configuration
@Conditional(OnDevelopmentEnvironmentCondition.class)
public class DevelopmentConfig {
// 配置用於開發環境的 bean
}

以下舉例傳入 Conditional 的類,檢查 myBean 是否存在於容器中,並返回布林值

// OnDevelopmentEnvironmentCondition.java

public class OnDevelopmentEnvironmentCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getBeanFactory().containsBean("myBean");
}
}

還可以看另一篇關於基本註解的文章 👉 點我
如果這篇文章有幫助到你,請不吝給我個鼓掌並看看我的其他文章吧😎

參考:40 個 SpringBoot 常用註解

--

--