注解的優勢:
- 采用純 java 代碼,不在需要配置繁雜的 xml 文件
- 在配置中也可享受面向對象帶來的好處
- 類型安全對重構可以提供良好的支持
- 減少復雜配置文件的同時亦能享受到 springIoC 容器提供的功能
1. 常用的Spring Boot注釋及其用途和示例
1)@SpringBootApplication
這是一個組合注解,它包含了 @Configuration,@EnableAutoConfiguration 和 @ComponentScan 三個注解。它的作用是標記 Spring Boot 應用的主類,讓 Spring Boot 自動進行必要的配置,掃描并加載符合條件的組件或 bean 定義,啟動內嵌的 web 服務器等。一般來說,我們只需要在主類上添加這個注解,就可以快速啟動一個 Spring Boot 應用。
@SpringBootApplication
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}
2)@RestController
這是一個組合注解,它包含了 @Controller 和 @ResponseBody 兩個注解。它的作用是標記一個控制器類,表示該類的所有方法的返回值都直接寫入 HTTP 響應體中,而不是解析為跳轉路徑。這個注解通常用于構建 RESTful 的 API,返回 JSON 或 XML 格式的數據。
@RestController
@RequestMapping("/users")
public class UserController {@Autowiredprivate UserService userService;@GetMapping("/{id}")public User getUser(@PathVariable("id") Long id) {return userService.getUserById(id);}
}
3)@RequestMapping
這是一個用于映射 HTTP 請求的注解,它可以用在類或方法上。它的作用是指定一個或多個請求路徑,以及對應的請求方法,請求參數,請求頭等條件,將其綁定到一個控制器方法上。它還可以指定一個返回值的媒體類型,以及一個視圖名稱等屬性。
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String hello() {return "Hello, Spring Boot!";
}
4)@GetMapping、@PostMapping、 @PutMapping、 @DeleteMapping、@PatchMapping
這些注解都是 @RequestMapping 的縮寫,它們分別對應了 GET, POST, PUT, DELETE, PATCH 這五種 HTTP 請求方法。它們的作用是簡化 @RequestMapping 的寫法,只需要指定一個請求路徑即可,其他屬性都有默認值。它們的用法和 @RequestMapping 類似,只是更加簡潔。
@GetMapping("/hello")
public String hello() {return "Hello, Spring Boot!";
}@PostMapping("/users")
public User createUser(@RequestBody User user) {return userService.saveUser(user);
}
5)@PathVariable、@RequestParam、@RequestBody、 @RequestHeader、 @CookieValue
這些注解都是用于獲取 HTTP 請求中的數據的注解,它們可以用在控制器方法的參數上。它們的作用是分別從請求路徑,請求參數,請求體,請求頭,或 Cookie 中獲取數據,并將其綁定到方法參數上。它們都可以指定一個參數名,以及一個是否必須的屬性。
@GetMapping("/users/{id}")
public User getUser(@PathVariable("id") Long id) {return userService.getUserById(id);
}@GetMapping("/users")
public List<User> getUsers(@RequestParam(value = "name", required = false) String name) {return userService.getUsersByName(name);
}@PostMapping("/users")
public User createUser(@RequestBody User user) {return userService.saveUser(user);
}@GetMapping("/hello")
public String hello(@RequestHeader("User-Agent") String userAgent) {return "Hello, your user agent is: " + userAgent;
}@GetMapping("/cookie")
public String cookie(@CookieValue("JSESSIONID") String sessionId) {return "Your session id is: " + sessionId;
}
6)@Autowired、@Resource、@Inject
這些注解都是用于實現依賴注入的注解,它們可以用在字段,構造器,或方法上。它們的作用是從 Spring 容器中獲取一個 bean 實例,并將其賦值給被注解的元素。它們的區別是:
- @Autowired 是 Spring 提供的注解,它默認按照類型匹配 bean,也可以通過 @Qualifier 指定按照名稱匹配。
- @Resource 是 Java 標準提供的注解,它默認按照名稱匹配 bean,也可以通過 type 屬性指定按照類型匹配。
- @Inject 是 Java 標準提供的注解,它和 @Autowired 類似,都是按照類型匹配 bean,但是它需要依賴 JSR-330 的實現,如 Google Guice。
@Service
public class UserService {@Autowiredprivate UserDao userDao;@Resource(name = "userCache")private Cache userCache;@Injectprivate PasswordEncoder passwordEncoder;// ...
}
7)@Bean、@Component、 @Service、@Repository、 @Controller
這些注解都是用于聲明一個 bean 的注解,它們可以用在類上。它們的作用是讓 Spring 容器掃描并識別這些類,將其實例化并加入到容器中,以便其他組件可以使用。它們的區別是:
- @Bean 是用在方法上的,它表示該方法返回一個 bean 實例,方法所在的類必須被 @Configuration 注解標記。
- @Component 是一個通用的注解,它表示該類是一個組件,沒有特殊的功能。
- @Service 是一個業務邏輯層的注解,它表示該類是一個服務類,提供一些業務功能。
- @Repository 是一個數據訪問層的注解,它表示該類是一個數據訪問對象,提供一些數據操作功能。
- @Controller 是一個控制器層的注解,它表示該類是一個控制器類,處理 HTTP 請求。
@Configuration
public class AppConfig {@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}
}@Component
public class HelloComponent {public void sayHello() {System.out.println("Hello, Spring Boot!");}
}@Service
public class UserService {// ...
}@Repository
public class UserDao {// ...
}@Controller
public class UserController {// ...
}
8)@Value、 @ConfigurationProperties、 @PropertySource
這些注解都是用于配置屬性的注解,它們可以用在類或字段上。它們的作用是從配置文件或環境變量中獲取一些屬性值,并將其賦值給被注解的元素。它們的區別是:
- @Value 是用于獲取單個屬性值的,它可以使用 ${…} 占位符或 SpEL 表達式來獲取屬性值,也可以指定一個默認值。
- @ConfigurationProperties 是用于獲取一組屬性值的,它可以使用 prefix 屬性來指定一個屬性前綴,然后將其下的所有屬性值綁定到一個 bean 的字段上,支持松散綁定和類型轉換,也支持 JSR-303 的數據校驗。
- @PropertySource 是用于指定一個額外的屬性源的,它可以使用 value 屬性來指定一個或多個屬性文件的路徑,然后將其加載到 Spring 環境中,以便其他注解可以使用。
@Component
@PropertySource("classpath:app.properties")
public class AppConfig {@Value("${app.name}")private String appName;@Value("${app.version:1.0}")private String appVersion;@Value("#{systemProperties['os.name']}")private String osName;// ...
}@Component
@ConfigurationProperties(prefix = "user")
@Validated
public class UserConfig {@NotBlankprivate String name;@Min(18)private int age;private List<String> hobbies;// getters and setters
}
9)@EnableAutoConfiguration
此注解用于啟用 Spring Boot 的自動配置機制。它根據類路徑依賴項和屬性自動配置應用程序。他可以簡化配置過程,從而實現快速開發。
例如
@SpringBootApplication
@EnableAutoConfiguration
public class MyApplication {// ...
}
有@EnableAutoConfiguration的情況下:
- Spring Boot將會根據項目的依賴和配置,自動配置應用程序的各個組件,例如數據源、JPA、Web等。
- MyService類會被自動掃描并納入Spring容器管理。
沒有@EnableAutoConfiguration的情況下:
- 我們需要手動配置應用程序的各個組件,例如配置數據源、JPA、Web等,這會增加開發工作量。
- MyService類不會被自動掃描,需要顯式配置才能被Spring容器管理
如果發現正在應用不需要的特定自動配置類,則可以使用 @EnableAutoConfiguration 的 exclude 屬性 來禁用它們
例如
@EnableAutoConfiguration(excludeName = {"org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration",
"org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration"})
10)@ConditionalOnProperty
此注解用于根據屬性的值有條件地啟用或禁用 bean 或配置。
例如
@Configuration
@ConditionalOnProperty(name = "my.feature.enabled", havingValue = "true")
public class MyConfiguration {// 當啟用時的功能配置
}
11)@Scheduled
此注解用于以固定的時間間隔調度方法的執行。
例如
@Component
public class MyScheduler {@Scheduled(fixedDelay = 5000)public void doSomething() {// 定期執行任務}
}
12)@Cacheable、@CachePut、@CacheEvict
這些注解用于緩存方法結果。它們允許您分別緩存方法的返回值、更新緩存或去除緩存。
@Service
public class MyService {@Cacheable("users")public User getUserById(Long id) {// 從數據庫檢索用戶}@CachePut("users")public User updateUser(User user) {// 更新數據庫和緩存中的用戶}@CacheEvict("users")public void deleteUser(Long id) {// 從數據庫刪除用戶并從緩存中移除}
}
13)@PostConstruct
@PostConstruct注解用于在依賴注入完成后,且在類的初始化之前執行標注的方法。這提供了一個非常方便的生命周期鉤子,允許開發者在對象創建和依賴注入完成后執行初始化代碼。
@Component
public class MyBean {@PostConstructpublic void init() {// 執行初始化操作,如加載配置文件,檢查資源等System.out.println("MyBean is initialized");}
}
在上述代碼中,init方法會在MyBean對象創建并注入所有必要的依賴之后被自動調用。
14)@ExceptionHandler、@ControllerAdvice
@ExceptionHandler注解用于處理Controller層拋出的異常。通過將此注解應用于方法上,可以捕獲特定類型的異常,并對其進行自定義處理。
@ControllerAdvice最常見的用途之一是全局異常處理。通過在類上使用@ControllerAdvice注解,然后在該類中使用@ExceptionHandler注解標注的方法,可以捕獲指定類型的異常,并進行處理。
@ControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(value = Exception.class)public ResponseEntity<Object> handleException(Exception e) {// 構建自定義的響應體,如錯誤信息和錯誤碼return new ResponseEntity<>("An error occurred", HttpStatus.INTERNAL_SERVER_ERROR);}
}
2. 面向切面的編程 (AOP) 注解
這些注解通常用于Spring框架中的面向切面編程(AOP,Aspect-Oriented Programming),于實現面向切面編程,將橫切關注點從核心業務邏輯中分離出來,以提高代碼的可維護性和可重用性。
1)@Aspect
這個注解用于聲明一個類是一個切面類,定義切面類的時候需要添加這個注解。切面類是用來封裝切面邏輯的類,通常包含一個或多個通知方法,以及一個或多個切點表達式。切面類需要被 Spring 容器管理,因此通常也需要添加 @Component 注解。例如:
@Aspect
@Component
public class LogAspect {// 通知方法和切點表達式
}
2)@Pointcut
這個注解用于定義一個切點表達式,指定哪些連接點(即方法執行的點)需要被攔截。切點表達式可以使用 AspectJ 的語法,也可以使用自定義的注解。切點表達式通常定義在一個空的方法上,作為一個標識符,供其他注解引用。例如:
@Pointcut("execution(* com.example.service.*.*(..))")
public void servicePointcut() {// 空方法,僅用于標識切點
}
這個注解表示,所有 com.example.service 包下的類的所有方法都是切點,需要被攔截。
3)@Before, @After, @AfterReturning, @AfterThrowing, @Around
這些注解都是用于定義通知方法的,指定在切點執行的不同階段執行不同的邏輯。通知方法是用來實現切面功能的方法,通常需要引用一個切點表達式,或者一個切點標識符。這些注解的含義和用法如下:
- @Before:表示在切點之前執行,可以用來做一些前置處理,如參數校驗,日志打印等。
- @After:表示在切點之后執行,無論切點是否正常返回或拋出異常,都會執行,可以用來做一些清理工作,如釋放資源,還原狀態等。
- @AfterReturning:表示在切點正常返回之后執行,可以用來做一些后置處理,如返回結果處理,日志記錄等。
- @AfterThrowing:表示在切點拋出異常之后執行,可以用來做一些異常處理,如異常日志記錄,事務回滾等。
- @Around:表示環繞切點執行,可以在切點前后執行自定義的邏輯,也可以控制切點是否執行,以及修改切點的參數和返回值,是最強大也最復雜的通知類型。
例如:
@Before("servicePointcut()")
public void beforeAdvice(JoinPoint joinPoint) {// 獲取方法簽名和參數MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();Object[] args = joinPoint.getArgs();// 打印方法信息和參數System.out.println("Before advice: " + method + ", args: " + Arrays.toString(args));
}@AfterReturning(value = "servicePointcut()", returning = "result")
public void afterReturningAdvice(JoinPoint joinPoint, Object result) {// 獲取方法簽名和返回值MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();// 打印方法信息和返回值System.out.println("After returning advice: " + method + ", result: " + result);
}@AfterThrowing(value = "servicePointcut()", throwing = "ex")
public void afterThrowingAdvice(JoinPoint joinPoint, Exception ex) {// 獲取方法簽名和異常MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();// 打印方法信息和異常System.out.println("After throwing advice: " + method + ", exception: " + ex.getMessage());
}@Around("servicePointcut()")
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {// 獲取方法簽名MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();// 記錄方法開始時間long startTime = System.currentTimeMillis();// 執行切點方法Object result = joinPoint.proceed();// 記錄方法結束時間long endTime = System.currentTimeMillis();// 計算方法耗時long duration = endTime - startTime;// 打印方法信息和耗時System.out.println("Around advice: " + method + ", duration: " + duration + " ms");// 返回結果return result;
}
4)@Order
這個注解用于指定切面類的優先級,當有多個切面類作用于同一個切點時,可以用這個注解來控制執行順序。注解的值越小,優先級越高。例如:
@Aspect
@Component
@Order(1)
public class LogAspect {// ...
}@Aspect
@Component
@Order(2)
public class SecurityAspect {// ...
}
這個注解表示,LogAspect 的優先級高于 SecurityAspect,因此 LogAspect 的通知方法會先于 SecurityAspect 的通知方法執行。
3. 驗證注釋
? 這些注解通常用于Java中的Bean Validation(JSR-380)規范中,用于對JavaBean屬性進行驗證
@Valid、@NotNull、@Size、@Min、@Max、@Email、@Pattern
- @Valid,用于指示在驗證嵌套對象時應該遞歸執行驗證。通常與復雜對象的屬性一起使用,以確保嵌套對象的所有屬性都被驗證。
- @NotNull,用于驗證屬性值不能為null。
- @Size,用于驗證屬性值的長度是否在指定范圍內。
- @Min,用于驗證屬性值是否大于等于指定的最小值。
- @Max,用于驗證屬性值是否小于等于指定的最大值。
- @Email,用于驗證屬性值是否符合Email地址的格式。
- @Pattern,用于驗證屬性值是否匹配指定的正則表達式。
例如
public class Address {@NotNullprivate String street;// 其他屬性和方法
}public class User {@Validprivate Address address;@NotNullprivate String username;@Size(min = 2, max = 50)private String username;@Min(18)private int age;@Max(100)private int age;@Emailprivate String email;@Pattern(regexp = "^[A-Za-z0-9]+$")private String username;// 其他屬性和方法
}