Spring 在 2.5 以後,不用集中式的 xml 設定檔,改用 annotation 定義,傳統的 XML 設定冗長,改用 annotation 這個方法後有好有壞,這讓開發者專注在程式開發,但會有不知道為什麼這樣設定就能運作的錯覺。
以下是 Spring Application 能看到的 annotations
Core Spring Framework
@Autowired
可用在 fields, setter methods, constructors,能夠直接 inject object dependency
fields: 透過 property name 自動設定 field value
public class Customer {
@Autowired
private Person person;
private int type;
}
setter: 告訴 Spring 要在 init bean 時,呼叫 setter method
public class Customer {
private Person person;
@Autowired
public void setPerson(Person person) {
this.person=person;
}
}
constructor: 產生物件時,做 injection,只能有一個 constructor 帶有 @Autowired annotation
@Component
public class Customer {
private Person person;
@Autowired
public Customer (Person person) {
this.person=person;
}
}
@Qualifier
跟 @Autowired 一起使用,當該 @Bean 有多個 instance 時,可透過 @Qualifier 指定 instance
如果有兩個 BeanInterface 的 instances
@Component
public class BeanB1 implements BeanInterface {
//
}
@Component
public class BeanB2 implements BeanInterface {
//
}
透過 @Qualifier("beanB2")
指定 beanB2
@Component
public class BeanA {
@Autowired
@Qualifier("beanB2")
private IBean dependency;
...
}
@Configuration
類似以往的 XML config file (applicationContext.xml),用來設定 Spring IoC beans
ex:
@Configuartion
public class DataConfig {
@Bean
public DataSource source() {
DataSource source = new OracleDataSource();
source.setURL();
source.setUser();
return source;
}
@Bean
public PlatformTransactionManager manager() {
PlatformTransactionManager manager = new BasicDataSourceTransactionManager();
manager.setDataSource(source());
return manager;
}
}
@SpringBootApplication 本身就包含了 @Configuartion,可直接在裡面使用 @Bean
ex:
@SpringBootApplication
public class SpringBootBeanDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootBeanDemoApplication.class, args);
}
@Bean
public FooService fooService() {
return new FooService();
}
}
@ComponentScan
跟著 @Configuration 一起使用
告訴 Spring 去哪一個 package 掃描 annotated components
@ComponentScan("com.test.config")
@Configuration
public class AppConfig {
}
可以指定 basePackageClasses 或 basePackages,取代直接設定 package。這樣可避免 class package 異動時,還要修改 @ComponentScan 的 package name
@ComponentScan(basePackageClasses = ApplicationController.class)
@Configuration
public class AppConfig {
}
@Bean
只能用在 method,跟 @Configuration 一起使用,告訴 Spring 產生哪些 bean
@Configuration
public class AppConfig {
@Bean
public Person person() {
return new Person(address());
}
@Bean
public Address address() {
return new Address();
}
}
@Lazy
用在 component class
autowired dependencies 預設會在啟動時,自動 created & configured
加上 @Lazy,表示該 bean 會在第一次被使用時,才被 created
@Lazy 也可以用在 @Configuration classes,表示所有的 @Bean 都會被延遲建立
@Value
用在 field, constructor parameter, method parameter
表示該 field/parameter 的初始值
通常 Spring 會自動載入 application.properties 的設定,如果是自訂 properties file,要用 @PropertySource 指定
ex:
#application.properties
user.name=admin
#my.properties
user.password=pwd123
@PropertySource("classpath:my.properties")
public class ValueController {
@Value("${user.name}")
private String name;
@Value("${user.password}")
private String password;
}
也可以指定 List, Array
tools=car,train,airplane
@Value("${tools}")
private String[] toolArray;
@Value("${tools}")
private List<String> toolList;
Spring Framework Stereotype Annotations
@Component
用在 class,代表一個 Spring component
@Component 可讓 Java class 在 component scanning 時被放入 application context
@Controller
代表 Spring controller
用在 Spring MVC 或 Spring WebFlux 的 controller
@Service
用在 class,讓 java class 提供某個 service,ex: 執行 business logic、計算、呼叫外部 API,這是一種用在 service layer 特殊的 @Component
@Repository
用在 Java class 可直接存取 DB,也就是 Data Access Object 的角色
有 auto translation 功能,ex: 發生 exception 時,會有 handler 處理該 exception,不需要加上 try-catch block
Spring Boot Annotations
@EnableAutoConfiguration
用在 main application class
會自動定義一個 base "search package",告訴 Spring Boot,根據 classpath 設定, perperties 設定,加入beans
會檢查所有 sub-packages & class
@Configuration
@EnableAutoConfiguration
public class EmployeeApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(EmployeeApplication.class, args);
// ...
}
}
排除 JdbcTemplateAutoConfiguration.class
@Configuration
@EnableAutoConfiguration(exclude={JdbcTemplateAutoConfiguration.class})
public class EmployeeApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(EmployeeApplication.class, args);
// ...
}
}
@SpringBootApplication
用在 application class,設定 Spring Boot project
會自動掃描 sub-packages,ex:
會掃描 com.example 的所有 sub-packages
package com.example;
@SpringBootApplication
public class EmployeeApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(EmployeeApplication.class, args);
// ...
}
}
使用 @SpringBootApplication 就等於使用了
- @Configuration
- @EnableAutoConfiguration
- @ComponentScan
Spring MVC and REST Annotations
@Controller
會自動偵測 classpath 的 component classes,並自動註冊 bean definitions
如果要偵測 annotated controllers,必須加入 component scanning 到 configuration。
@Controller 可處理多個 request mappings
@Controller 用在 Spring MVC 及 Spring WebFlux
@RequestMapping
可用在 class 及 method
用來 map web request 到某個 class / handler methods
用在 class level 時,會產生該 controler 的 base URI,然後就能使用所有 handler methods
如果要限制 HTTP method,可在 handler method 上加上 @RequestMapping,ex:
只接受 GET method request 發送到 /welcome
@Controller
@RequestMapping("/welcome")
public class WelcomeController {
@RequestMapping(method = RequestMethod.GET)
public String welcomeAll() {
return "welcome all";
}
}
用在 Spring MVC 及 Spring WebFlux
@CookieValue
用在 method parameter level
用在 request mapping method 的參數,透過 cookie name 綁定
ex:
當有個 http request 夾帶 JSESSIONID 的 cookie,可透過 @CookieValue 取得 JSESSIONID 的值
JSESSIONID=418AB76CD83EF94U85YD34W
@ReuestMapping("/cookieValue")
public void getCookieValue(@CookieValue "JSESSIONID" String cookie){
}
@CrossOrigin
用在 class 及 method level,可 enable cross-origin requests
如果可讓 js 從不同 host 取得資料,就要用 @CrossOrigin 啟用 cross-origin resource sharing
@CrossOrigin 預設允許所有 origins, headers,maxAge 為 30 min
ex:
getMessage 與 getNot 都會是 maxAge of 3600 seconds
getMessage 只允許來自 http://example.com 的 request
getNote 允許所有 hosts
@CrossOrigin(maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {
@CrossOrigin(origins = "http://example.com")
@RequestMapping("/message")
public Message getMessage() {
// ...
}
@RequestMapping("/note")
public Note getNote() {
// ...
}
}
Composed @RequestMapping Variants
用在 Spring MVC 及 WebFlux
@GetMapping
只接受 Get method request
跟以下 @RequestMapping 意義一樣
@RequestMapping(method = RequestMethod.GET)
@PostMapping
只接受 Postmethod request
跟以下 @RequestMapping 意義一樣
@RequestMapping(method = RequestMethod.POST)
@PutMapping
只接受 PUT method request
跟以下 @RequestMapping 意義一樣
@RequestMapping(method = RequestMethod.PUT)
@PatchMapping
只接受 Patch method request
跟以下 @RequestMapping 意義一樣
@RequestMapping(method = RequestMethod.PATCH)
@DeleteMapping
只接受 DELETE method request
跟以下 @RequestMapping 意義一樣
@RequestMapping(method = RequestMethod.DELETE)
@ExceptionHandler
處理 controller level 的 exception,定義該 class 要 catch 的 exception class
@ExceptionHandler 的 value 可以是 array of Expception types
@InitBinder
method-level
識別用來初始化 WebDataBinder 的 methods
WebDataBinder 是可將 request parameter 綁定 JavaBean objects 的 DataBinder
可在 controller 使用 @InitBinder annotated method
@InitBinder annotated methods 會在每一次 http request 被呼叫。用來驗證參數是否符合規則
ex: 如果有一個 request form 是 Student 資料
先產生 Student Java Bean
public class Student{
private String Id;
private String firstName;
@NotNull(message="is required")
@Size(min=1,message="is required")
private String lastName; // validation done to check if lastname is NULL
@Max(value=10,message="Value should between 0 and 10")
@Min(value=0,message="Value should between 0 and 10")
private String standard;
private String Age;
}
用 initBinder 註冊 StringTrimmerEditor
@InitBinder
public void initBinder(WebDataBinder dataBinder) {
StringTrimmerEditor stringTrimmerEditor = new StringTrimmerEditor(true);
dataBinder.registerCustomEditor(String.class, stringTrimmerEditor);
}
@Mappings and @Mapping
用在 fields
@Mappings 是 web mapping annotation,可對應 source field 到 target field
@MaxtrixVariable
用在 request handler method arguments
可 inject relevant bits of a maxtri URI
maxtrix variables 會以 semicolon 區隔
如果 URL 包含 matrix variables,request mapping pattern 會以 URI template 表示
http://localhost:8080/spring/employees/John;beginContactNumber=22001
用以下 method
@RequestMapping(value = "/employees/{name}", method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<List<Employee>> getEmployeeByNameAndBeginContactNumber(
@PathVariable String name, @MatrixVariable String beginContactNumber) {
List<Employee> employeesList = new ArrayList<Employee>();
...
return new ResponseEntity<>(employeesList, HttpStatus.OK);
}
URI
http://localhost:8080/spring/employees/id=1;name=John;contactNumber=2200112334
@GetMapping("employees/{employee}")
@ResponseBody
public ResponseEntity<Map<String, String>> getEmployeeData(
@MatrixVariable Map<String, String> matrixVars) {
return new ResponseEntity<>(matrixVars, HttpStatus.OK);
}
@PathVariable
用在 method arguments
處理 dynamic changes in URI
可以用 regular expression
ex:
@GetMapping("/api/employees/{id}/{name}")
@ResponseBody
public String getEmployeesByIdAndName(@PathVariable String id, @PathVariable String name) {
return "ID: " + id + ", name: " + name;
}
@RequestAttribute
綁定 request attribute 到 handler method paratmeter
ex: 計算 visit counter
Interceptor 增加 request attribute
public class MyCounterInterceptor extends HandlerInterceptorAdapter {
private AtomicInteger counter = new AtomicInteger(0);
@Override
public boolean preHandle (HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
request.setAttribute("visitorCounter", counter.incrementAndGet());
return true;
}
}
controller
@Controller
public class ExampleController {
@RequestMapping("/")
@ResponseBody
public String handle (@RequestAttribute("visitorCounter") Integer counter) {
return String.format("Visitor number: %d", counter);
}
}
JavaConfig
@EnableWebMvc
@ComponentScan("com.example")
public class AppConfig extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors (InterceptorRegistry registry) {
registry.addInterceptor(new MyCounterInterceptor());
}
}
ref: https://www.logicbig.com/tutorials/spring-framework/spring-web-mvc/request-attribute.html
@RequestBody
一般用於處理非 Content-Type: application/x-www-form-urlencoded 編碼格式的資料,例如:application/json、application/xml
@RequestMapping(path = "/something", method = RequestMethod.PUT)
public void handle(@RequestBody String body, Writer writer) throws IOException {
writer.write(body);
}
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value="/user", consumes = MediaType.APPLICATION_JSON_VALUE)
public void process2(@RequestBody User user) {
logger.info("User: {}", user);
}
將 request header 裡面的 name 對應到 handler method parameter
@RequestParam
在 controller 中,跟 @RequestMapping 一起使用,綁定 request parameter 到 method parameter
@RequestPart
取代 @RequestParam
可取得特定 multipart 的 content 到 method parameter
Content-type 必須要是 multipart
@ResponseBody
用在 handler methods,類似 @RequestBody
spring 會將物件透過 HttpMessageConverter 轉換為 JSON/XML 寫入response body
@ResponseStatus
用在 methods, exception class,指定回傳的 status code
@ControllerAdvice
class level
可定義@ExceptionHandler
, @InitBinder
, and @ModelAttribute
methods ,就會套用在所有 @RequestMapping
methods
如果在 @ControllerAdvice class 的某個 method 加上 @ExceptionHandler,會被套用在所有 controllers
@RestController
將 class 標記為 controller
使用 @RestController 就不需要在所有 RequestMapping methods 加上 @ResponseBody,這表示不會在 response 回傳 html
@RestControllerAdvice
等同 @ControllerAdvice 與 @ResponseBody,再加上 @ExceptionHandler 處理 exceptions
@SessionAttribute
method parameter level
綁定 method parameter to a session attribute
@SessionAttributes
type level
要跟 @ModelAttribute 一起使用
可以將 JavaBean object 增加到 session 中
@ModelAttribute("person")
public Person getPerson() {}
// within the same controller as above snippet
@Controller
@SeesionAttributes(value = "person", types = {
Person.class
})
public class PersonController {}
Spring Cloud Annotations
@EnableConfigServer
class level
當 project 有多個 services,需要集中設定所有 services
centralized config server 的優點是不需要知道 components 在哪裏
@EnableEurekaServer
用在 class
使用 microservices 時,很難知道每一個 service 相關的 addresses,必須要有 service discovery 方法
Netflix’s Eureka 是 discovery server 的實作
@EnableDiscoveryClient
用在 java class
通知哪一個 application 要註冊到 Eureka,會將 host, port 註冊到 Eureka
@EnableCircuitBreaker
用在 java class
circuit breaker pattern 讓 microservice 在相關 service fails 還能持續運作,避免 error 擴散
讓 service 有 recover 的時間
@HystrixCommand
method level
Netflix’s Hystrix library 是 Circuit Breaker pattern 的實作
當 method 套用 circuit breaker,Hystrix 會監控 failure of the method。當錯誤發生,Hystrix 會打開 circuit,後續的呼叫會 fail,Hystrix 會 redirect calls 到這個 method
ex:
@Service
public class BookService {
private final RestTemplate restTemplate;
public BookService(RestTemplate rest) {
this.restTemplate = rest;
}
@HystrixCommand(fallbackMethod = "newList") public String bookList() {
URI uri = URI.create("http://localhost:8081/recommended");
return this.restTemplate.getForObject(uri, String.class);
}
public String newList() {
return "Cloud native Java";
}
}
Spring Framework DataAccess Annotations
@Transactional
放在 interface 定義, interface 的 method, class 定義, class 的 public method 前面
@Transactional 只是一個 metadata,讓 beans 設定 tranactional behavior
Cache-Based Annotations
@Cacheable
用在 methods
可將 return 的資料以 addresses 儲存到 cache
@Cacheable("addresses")
public String getAddress(Book book){...}
每次該 method 被呼叫時,會先檢查 cache
@CachePut
當要 update cache 時,可用 @CachePut
@CachePut("addresses")
public String getAddress(Book book){...}
不建議將 @CachePut, @Cacheable 合併使用
@CacheEvict
用在 method
可將 cache 清空,allEntries 就是清除所有 values
@CacheEvict(value="addresses", allEntries="true")
public String getAddress(Book book){...}
@CacheConfig
class level
可儲存 cache configuration
Task Execution and Scheduling Annotations
@Scheduled
method level
該 method 必須回傳 void,且不能有任何參數
// 等 5s 後開始執行,會等前一次結束後再跑第二次
@Scheduled(fixedDelay=5000)
public void doSomething() {
// something that should execute periodically
}
// 每 5s 執行一次,不管前一次有沒有跑完
@Scheduled(fixedRate=5000)
public void doSomething2() {
// something that should execute periodically
}
@Scheduled(initialDelay=1000,fixedRate=5000)
public void doSomething3() {
// something that should execute periodically after an initial delay
}
@Async
method level
每個 method 都在不同 thread 執行
@Async 如果有 return value,必須要是 Future-typed
Spring Framework Testing Annotations
@BootstrapWith
class level
設定 Spring TestContext Framework 如何啟動
@ContextConfiguration
class level
宣告要載入 context 的 class,及設定的 xml
@ContextConfiguration(locations={"example/test-context.xml", loader = Custom ContextLoader.class})
@WebAppConfiguration
class level
ApplicationContext 要載入的必須要是 WebApplicationContext
@WebAppConfiguration 可產生 web version of the application context
default root of web app 為 src/main/webapp
@Timed
method
該method 必須在限定時間內執行結束。超時就代表測試失敗
@Timed(millis=10000)
public void testLongRunningProcess() { ... }
@Repeat
用在 test method
重複執行
@Repeat(10)
@Test
public void testProcessRepeatedly() { ... }
@Commit
class level or method level
執行後,transaction of the transactional test method 會被 commit
@RollBack
class level or method level
@Rollback(true)
, the transaction is rolled back
@DirtiesContext
class, method level
表示 ApplicationContext 已被 modified / corrupted
@BeforeTransaction
在 @Transactional 前執行
@AfterTransaction
在 @Transactional 後
@Sql
run SQL scripts against a database
@SqlConfig
決定 parse and execute SQL scripts 的方法
@SqlGroup
method level
a container annotation that can hold several @Sql
annotations
@SpringBootTest
start the Spring context for integration tests
@DataJpaTest
test Spring Data JPA using an in-memory database such as H2
@DataMongoTest
@WebMVCTest
a mock servlet context for testing the MVC layer
@MockBean
產生並 inject Mockito Mock
@JsonTest
限制測試處理 JSON 的 auto-configuration of Spring Boot 的 components
@TestPropertySource
class level
property sources for the test class
References
A Guide to Spring Framework Annotations
太厲害了!終於有人把Spring Boot常用註釋講明白了!
Top 10 Spring Framework Annotations for Java Programmers
Spring Annotation Note