Java — Spring Annotation
在閱讀網路上 Spring Boot 的專案時發現沒看過的註釋有點多,在不明白功用的情況下看得有點沒意思,先就常用的註釋理解一下
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 的參數解析並綁定到指定的對象,常用於處理 Post
、 Put
的函式參數,只支持 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");
}
}
還可以看另一篇關於基本註解的文章 👉 點我
如果這篇文章有幫助到你,請不吝給我個鼓掌並看看我的其他文章吧😎