Spring Boot常用注解速查表(30個(gè)必會(huì)注解+實(shí)戰(zhàn)案例)
一、前言
Spring Boot開發(fā)中,注解無處不在。但注解太多,容易忘記怎么用?
本文整理了30個(gè)最常用的注解,分為7大類,每個(gè)注解都配有:
- 作用說明
- 真實(shí)代碼示例(來自我的記賬系統(tǒng)項(xiàng)目)
- 使用場景
- 常見錯(cuò)誤
二、核心注解(5個(gè))
2.1 @SpringBootApplication
@SpringBootApplication 是 Spring Boot 核心組合注解,整合@Configuration、@EnableAutoConfiguration、@ComponentScan三大核心注解的功能,用于標(biāo)注 Spring Boot 項(xiàng)目的主啟動(dòng)類,是開啟 Spring Boot 自動(dòng)配置、組件掃描的核心入口,簡化項(xiàng)目配置,一鍵啟動(dòng) Spring Boot 應(yīng)用。
- 含
@Configuration:將類標(biāo)記為配置類,支持注解式配置; - 含
@EnableAutoConfiguration:開啟自動(dòng)配置,自動(dòng)加載適配的框架配置; - 含
@ComponentScan:掃描主類所在包及子包的 @Component 系列注解(@Controller/@Service /@Mapper等),將 Bean 納入容器。
實(shí)戰(zhàn)代碼:
package com.liu;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@MapperScan("com.liu.mapper") // 掃描Mapper接口
@SpringBootApplication // 核心注解
public class SimpleAccountingApplication {
public static void main(String[] args) {
SpringApplication.run(SimpleAccountingApplication.class, args);
}
}使用場景: 每個(gè)Spring Boot項(xiàng)目有且僅有一個(gè)啟動(dòng)類
常見錯(cuò)誤:
// 錯(cuò)誤:啟動(dòng)類放在子包里,掃描不到其他組件
package com.liu.config; // 錯(cuò)誤位置
@SpringBootApplication
public class Application { }
// 正確:啟動(dòng)類放在根包
package com.liu; // 正確位置
@SpringBootApplication
public class Application { }2.2 @Component
作用: @Component 是 Spring 框架的核心通用組件注解,用于標(biāo)記普通 Java 類為Spring 容器可管理的 Bean,標(biāo)注后類會(huì)被 Spring 掃描并實(shí)例化,納入 IoC 容器統(tǒng)一管理,實(shí)現(xiàn)對象的依賴注入與生命周期管控,解耦組件間依賴。
它是 @ComponentScan 掃描的基礎(chǔ)注解,@Controller、@Service、@Repository 等注解均基于它擴(kuò)展,分別適配控制層、業(yè)務(wù)層、數(shù)據(jù)層等場景,@Component 則適用于無明確分層的通用組件,是 Spring 組件化開發(fā)的基礎(chǔ)。
實(shí)戰(zhàn)代碼:
@Component
public class JwtUtil {
@Value("${jwt.secret}")
private String secret;
public String generateToken(Long userId, Integer role, String username) {
// 生成JWT Token
}
}使用場景: 工具類、攔截器、切面等
衍生注解:
@Controller:控制器@Service:服務(wù)層@Repository:數(shù)據(jù)訪問層@Configuration:配置類
2.3 @Autowired
作用: @Autowired 是 Spring 核心的自動(dòng)依賴注入注解,用于實(shí)現(xiàn) Spring IoC 容器中 Bean 的自動(dòng)裝配,簡化組件間依賴管理,無需手動(dòng)創(chuàng)建對象實(shí)例,直接注入所需依賴 Bean,大幅降低代碼耦合度。
其默認(rèn)按類型(byType) 匹配容器中的 Bean 完成注入,支持標(biāo)注在類的構(gòu)造方法、字段、setter 方法上;若同類型 Bean 存在多個(gè),可結(jié)合 @Qualifier 按名稱精準(zhǔn)匹配,或用 @Primary 指定默認(rèn)注入 Bean。
實(shí)戰(zhàn)代碼:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper; // 自動(dòng)注入Mapper
@Autowired
private JwtUtil jwtUtil; // 自動(dòng)注入工具類
public User login(String username, String password) {
return userMapper.selectByUsername(username);
}
}使用場景: 注入Service、Mapper、工具類等
推薦寫法(構(gòu)造器注入):
@Service
@RequiredArgsConstructor // Lombok注解,自動(dòng)生成構(gòu)造器
public class UserServiceImpl implements UserService {
private final UserMapper userMapper; // final修飾
private final JwtUtil jwtUtil;
// Lombok自動(dòng)生成構(gòu)造器,Spring自動(dòng)注入
}為什么推薦構(gòu)造器注入?
- 不可變(final修飾)
- 避免循環(huán)依賴
- 便于單元測試
2.4 @Value
作用: 注入配置文件中的值。@Value 是 Spring 框架的屬性注入注解,核心用于將配置文件值、系統(tǒng)屬性、常量值注入到 Spring 容器管理的 Bean 的字段 / 方法參數(shù)中,實(shí)現(xiàn)配置與代碼解耦,無需硬編碼配置信息,簡化配置讀取與使用。
支持直接注入常量(如@Value("hello")),更常用的是通過${key}讀取 application.properties/yaml 中的配置項(xiàng)(如@Value("${server.port}")),可標(biāo)注在 Bean 的字段、構(gòu)造方法參數(shù)、setter 方法上,適配 Spring 管理的所有組件(@Component/@Service/@Controller 等)。
實(shí)戰(zhàn)代碼:
@Component
public class JwtUtil {
@Value("${jwt.secret}")
private String secret; // 注入配置:jwt.secret
@Value("${jwt.expiration}")
private Long expiration; // 注入配置:jwt.expiration
}配置文件(application.yml):
jwt: secret: my-secret-key-12345 expiration: 7200000 # 2小時(shí)
使用場景: 注入配置參數(shù)(密鑰、超時(shí)時(shí)間、文件路徑等)
常見錯(cuò)誤:
// 錯(cuò)誤:配置文件中沒有這個(gè)key
@Value("${jwt.secretKey}") // 配置文件是jwt.secret,不是jwt.secretKey
private String secret;
// 正確:設(shè)置默認(rèn)值
@Value("${jwt.secret:default-secret}") // 如果沒有配置,使用default-secret
private String secret;2.5 @Configuration
作用: @Configuration 是 Spring 框架的核心配置類注解,用于標(biāo)記 Java 類為Spring 配置類,替代傳統(tǒng) XML 配置文件,實(shí)現(xiàn)純注解式配置開發(fā)。
標(biāo)注該注解的類會(huì)被 Spring 解析,類中通過@Bean注解修飾的方法,其返回對象會(huì)被注冊為 Spring IoC 容器中的 Bean,由容器統(tǒng)一管理實(shí)例化、依賴注入與生命周期,是 Spring 注解驅(qū)動(dòng)開發(fā)的核心基礎(chǔ)。
實(shí)戰(zhàn)代碼:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private JwtInterceptor jwtInterceptor;
// 注冊攔截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtInterceptor)
.addPathPatterns("/**");
}
// 配置跨域
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowedMethods("GET", "POST", "DELETE", "PUT");
}
}使用場景: 配置攔截器、跨域、數(shù)據(jù)源、線程池等
三、Web層注解(8個(gè))
3.1 @RestController
作用: @RestController 是 Spring MVC 的組合注解,整合@Controller和@ResponseBody核心功能,專門用于標(biāo)注RESTful 風(fēng)格的控制器類,是 Spring Boot 開發(fā)接口的核心注解,無需額外配置即可實(shí)現(xiàn) JSON/XML 等數(shù)據(jù)的返回。
- 繼承
@Controller:將類標(biāo)記為 Spring MVC 控制器,接收前端請求; - 整合
@ResponseBody:默認(rèn)對類中所有 @RequestMapping 系列注解的方法生效,自動(dòng)將方法返回值序列化為 JSON/XML 格式,直接寫入響應(yīng)體,替代傳統(tǒng)視圖跳轉(zhuǎn),適配前后端分離場景。
對比單獨(dú)使用@Controller+ 方法級(jí)@ResponseBody,該注解簡化了 REST 接口的開發(fā),無需在每個(gè)接口方法上重復(fù)標(biāo)注@ResponseBody。
等價(jià)于: @Controller + @ResponseBody
實(shí)戰(zhàn)代碼:
@RestController
@RequestMapping("/user")
public class UserController {
@PostMapping("/login")
public Result<String> login(@RequestParam String username,
@RequestParam String password) {
// 返回JSON:{"code":200,"msg":"登錄成功","data":"token"}
return Result.success("登錄成功", token);
}
}使用場景: 前后端分離項(xiàng)目,返回JSON數(shù)據(jù)
對比:
| 注解 | 返回類型 | 使用場景 |
|---|---|---|
@Controller | 視圖(HTML) | 傳統(tǒng)MVC項(xiàng)目 |
@RestController | JSON數(shù)據(jù) | 前后端分離項(xiàng)目 |
3.2 @RequestMapping
作用: @RequestMapping 是 Spring MVC核心請求映射注解,用于將前端 HTTP 請求與后端控制器方法建立綁定關(guān)系,使 Spring 能精準(zhǔn)匹配請求并調(diào)用對應(yīng)方法處理,是開發(fā) Web 接口 / 頁面請求的基礎(chǔ)注解,可標(biāo)注在控制器類或方法上。
實(shí)戰(zhàn)代碼:
@RestController
@RequestMapping("/user") // 類級(jí)別:所有方法的URL前綴
public class UserController {
// 方法級(jí)別:完整URL = /user/login
@RequestMapping(value = "/login", method = {RequestMethod.GET, RequestMethod.POST})
public Result<String> login() {
// 支持GET和POST請求
}
}簡化衍生注解(Spring MVC 推薦)
為簡化method屬性配置,Spring 提供了 @RequestMapping 的專用衍生注解,語義更清晰,開發(fā)中優(yōu)先使用:
@GetMapping:等價(jià)于@RequestMapping(method = RequestMethod.GET),處理 GET 查詢請求;@PostMapping:等價(jià)于@RequestMapping(method = RequestMethod.POST),處理 POST 新增 / 提交請求;@PutMapping:等價(jià)于 @RequestMapping(method = RequestMethod.PUT),處理 PUT 更新請求;@DeleteMapping:等價(jià)于 @RequestMapping(method = RequestMethod.DELETE),處理 DELETE 刪除請求。
@GetMapping("/list") // 等價(jià)于 @RequestMapping(method = RequestMethod.GET)
@PostMapping("/add") // 等價(jià)于 @RequestMapping(method = RequestMethod.POST)
@PutMapping("/update") // 等價(jià)于 @RequestMapping(method = RequestMethod.PUT)
@DeleteMapping("/delete")// 等價(jià)于 @RequestMapping(method = RequestMethod.DELETE)
3.3 @RequestParam
作用: 獲取URL參數(shù)(?key=value)
實(shí)戰(zhàn)代碼:
@PostMapping("/login")
public Result<String> login(@RequestParam String username,
@RequestParam String password) {
// 請求:POST /user/login?username=admin&password=123456
// username = "admin"
// password = "123456"
}可選參數(shù):
@GetMapping("/list")
public Result<List<Bill>> list(
@RequestParam(required = false) String keyword, // 可選參數(shù)
@RequestParam(defaultValue = "1") Integer page // 默認(rèn)值
) {
// 請求:GET /bill/list
// keyword = null
// page = 1
}
3.4 @RequestBody
作用:
@RequestBody 是 Spring MVC 的核心請求體解析注解,專門用于接收前端通過 HTTP 請求體傳遞的非表單格式數(shù)據(jù)(如 JSON、XML),并自動(dòng)將請求體中的數(shù)據(jù)反序列化為指定的 Java 實(shí)體類 / Map / 字符串對象,綁定到控制器方法的參數(shù)上,是前后端分離場景中接收 JSON 請求的必備注解。
實(shí)戰(zhàn)代碼:
@PostMapping("/register")
public Result<User> register(@RequestBody User user) {
// 請求體:{"username":"admin","password":"123456"}
// 自動(dòng)轉(zhuǎn)換為User對象
// user.getUsername() = "admin"
// user.getPassword() = "123456"
}
使用場景: POST/PUT請求,提交JSON數(shù)據(jù)
常見錯(cuò)誤:
// 錯(cuò)誤:GET請求不能用@RequestBody
@GetMapping("/list")
public Result<List<Bill>> list(@RequestBody Map<String, Object> params) {
// GET請求沒有請求體,會(huì)報(bào)錯(cuò)
}
// 正確:GET請求用@RequestParam
@GetMapping("/list")
public Result<List<Bill>> list(@RequestParam String keyword) {
// 正確
}3.5 @PathVariable
作用:
@PathVariable 是 Spring MVC 的路徑參數(shù)綁定注解,核心用于提取 URL 路徑中的動(dòng)態(tài)占位符參數(shù),并自動(dòng)將其轉(zhuǎn)換為指定類型后綁定到控制器方法的入?yún)⑸希情_發(fā)RESTful 風(fēng)格動(dòng)態(tài)接口的核心注解(如/user/123、/order/456/detail這類含動(dòng)態(tài) ID 的請求)。
實(shí)戰(zhàn)代碼:
@DeleteMapping("/delete/{id}")
public Result<Boolean> delete(@PathVariable Long id) {
// 請求:DELETE /bill/delete/123
// id = 123
billService.deleteById(id);
return Result.success("刪除成功");
}
@GetMapping("/user/{userId}/bill/{billId}")
public Result<Bill> getBill(@PathVariable Long userId,
@PathVariable Long billId) {
// 請求:GET /user/10/bill/20
// userId = 10
// billId = 20
}使用場景: RESTful風(fēng)格的URL
3.6 @ResponseBody
作用: @ResponseBody 是 Spring MVC 的核心響應(yīng)體處理注解,核心作用是將控制器方法的返回值,直接序列化為 JSON/XML 等格式的數(shù)據(jù)流,寫入 HTTP 響應(yīng)體(Response Body),替代傳統(tǒng)的視圖跳轉(zhuǎn)(如跳轉(zhuǎn)到 JSP/HTML 頁面),是實(shí)現(xiàn)前后端分離接口和RESTful 風(fēng)格接口的關(guān)鍵注解。
核心使用特性
- 生效范圍:可標(biāo)注在控制器方法上(僅對當(dāng)前方法生效),也可標(biāo)注在控制器類上(對類中所有 @RequestMapping 系列注解的方法生效);
- 自動(dòng)序列化:Spring 默認(rèn)通過 Jackson 框架完成轉(zhuǎn)換,將 Java 對象(實(shí)體類、Map、集合等)自動(dòng)序列化為 JSON 字符串,無需手動(dòng)處理;
- 返回值適配:支持所有 Java 基礎(chǔ)類型、自定義實(shí)體類、Map、List 等,均能被自動(dòng)序列化;
- 與 @RestController 的關(guān)系:
@RestController是@Controller + @ResponseBody的組合注解,標(biāo)注 @RestController 的類,底層已默認(rèn)添加類級(jí)別的 @ResponseBody,無需重復(fù)標(biāo)注。
實(shí)戰(zhàn)代碼:
@Controller // 注意:不是@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/toLogin")
public String toLogin() {
return "redirect:/login.html"; // 返回視圖
}
@ResponseBody // 單獨(dú)使用,返回JSON
@PostMapping("/login")
public Result<String> login() {
return Result.success("登錄成功"); // 返回JSON
}
}使用場景: 混合使用視圖和JSON返回時(shí)
3.7 @Valid / @Validated
作用: @Valid 和 @Validated 均是 Spring 中用于開啟參數(shù)校驗(yàn)的核心注解,基于 JSR-380(Bean Validation 2.0)規(guī)范實(shí)現(xiàn),配合 @NotNull/@NotBlank/@Min 等校驗(yàn)注解,可自動(dòng)完成對實(shí)體類參數(shù)的合法性校驗(yàn),替代手動(dòng) if 判斷,簡化參數(shù)校驗(yàn)邏輯,是開發(fā)接口時(shí)的必備注解。
二者核心作用一致,但所屬規(guī)范、功能特性、適用場景存在明確區(qū)別,其中 @Validated 是 Spring 對 @Valid 的增強(qiáng)擴(kuò)展版,開發(fā)中可根據(jù)需求選擇。
實(shí)戰(zhàn)代碼:
// 實(shí)體類(User.java)
@Data
public class User {
@NotBlank(message = "用戶名不能為空")
@Size(min = 3, max = 20, message = "用戶名長度必須在3-20個(gè)字符之間")
private String username;
@NotBlank(message = "密碼不能為空")
@Size(min = 6, max = 10, message = "密碼長度必須在6-10個(gè)字符之間")
@Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d]{6,10}$",
message = "密碼必須包含大小寫字母和數(shù)字")
private String password;
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手機(jī)號(hào)格式不正確")
private String phone;
}
// 控制器
@PostMapping("/register")
public Result<User> register(@RequestBody @Valid User user) {
// 如果校驗(yàn)失敗,自動(dòng)返回400錯(cuò)誤,不會(huì)執(zhí)行方法體
// 如果校驗(yàn)成功,繼續(xù)執(zhí)行
return userService.register(user);
}常用校驗(yàn)注解:
在博主以前的文章里面有詳細(xì)的JSR303校驗(yàn)講解: JSR303 Bean Validation 詳解(權(quán)威版).md
| 注解 | 作用 | 示例 |
|---|---|---|
@NotNull | 不能為null | @NotNull(message = "ID不能為空") |
@NotBlank | 不能為空字符串 | @NotBlank(message = "用戶名不能為空") |
@Size | 長度限制 | @Size(min = 3, max = 20) |
@Pattern | 正則驗(yàn)證 | @Pattern(regexp = "^1[3-9]\\d{9}$") |
@Min / @Max | 數(shù)值范圍 | @Min(value = 0, message = "金額不能為負(fù)") |
@Email | 郵箱格式 | @Email(message = "郵箱格式不正確") |
3.8 @ExceptionHandler
作用: @ExceptionHandler 是 Spring MVC 提供的異常處理核心注解,核心作用是捕獲并處理 Spring 容器中指定類型的異常,可標(biāo)注在控制器(@Controller/@RestController)或全局異常處理器的方法上,實(shí)現(xiàn)異常的局部 / 全局統(tǒng)一處理,替代傳統(tǒng)的 try-catch 代碼塊,簡化異常處理邏輯,讓接口返回統(tǒng)一、友好的錯(cuò)誤響應(yīng)。
核心核心特性
- 精準(zhǔn)捕獲異常:通過注解參數(shù)指定要處理的單個(gè) / 多個(gè)異常類型(如
@ExceptionHandler(NullPointerException.class)),僅捕獲匹配的異常; - 局部 / 全局生效:
- 標(biāo)注在普通控制器類中:僅處理當(dāng)前控制器中方法拋出的異常(局部生效);
- 標(biāo)注在標(biāo)注了 @RestControllerAdvice/@ControllerAdvice 的全局異常類中:處理整個(gè)項(xiàng)目所有控制器拋出的異常(全局生效,開發(fā)中最常用);
- 靈活返回結(jié)果:方法返回值支持與普通控制器方法一致的類型(如 JSON 對象、視圖名稱),適配前后端分離(返回 JSON)和傳統(tǒng)視圖開發(fā)場景;
- 自動(dòng)注入異常對象:方法入?yún)⒖芍苯咏邮?strong>捕獲到的異常實(shí)例,便于獲取異常信息(如錯(cuò)誤消息、堆棧軌跡)。
實(shí)戰(zhàn)代碼:
@RestControllerAdvice // 全局異常處理
public class GlobalExceptionHandler {
// 處理參數(shù)校驗(yàn)異常
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result<String> handleValidException(MethodArgumentNotValidException e) {
String message = e.getBindingResult().getFieldError().getDefaultMessage();
return Result.fail(message); // 返回:{"code":400,"msg":"用戶名不能為空"}
}
// 處理業(yè)務(wù)異常
@ExceptionHandler(RuntimeException.class)
public Result<String> handleRuntimeException(RuntimeException e) {
return Result.fail(e.getMessage());
}
// 處理數(shù)據(jù)庫唯一約束異常
@ExceptionHandler(SQLIntegrityConstraintViolationException.class)
public Result<String> handleSQLException(SQLIntegrityConstraintViolationException e) {
if (e.getMessage().contains("Duplicate entry")) {
return Result.fail("數(shù)據(jù)已存在");
}
return Result.fail("數(shù)據(jù)庫操作失敗");
}
}使用場景: 統(tǒng)一處理異常,避免在每個(gè)方法中try-catch
類上標(biāo)注的是全局異常處理注解:@RestControllerAdvice 是 Spring MVC 提供的全局異常處理 + 數(shù)據(jù)綁定 + 返回值增強(qiáng)的核心組合注解,是 @ControllerAdvice + @ResponseBody 的整合版,專門適配前后端分離項(xiàng)目,核心作用是實(shí)現(xiàn)全局統(tǒng)一的異常處理、全局?jǐn)?shù)據(jù)綁定、全局返回值封裝,無需在每個(gè)控制器重復(fù)編寫邏輯,讓接口返回格式統(tǒng)一、異常處理更簡潔。
四、Service層注解(2個(gè))
4.1 @Service
作用: @Service 是 Spring 框架中業(yè)務(wù)層組件專用注解,屬于 @Component 的分層擴(kuò)展注解,核心作用是標(biāo)記普通 Java 類為Spring IoC 容器可管理的業(yè)務(wù)層 Bean,讓類被 @ComponentScan 自動(dòng)掃描并納入容器統(tǒng)一管理,是 Spring 分層開發(fā)(控制層 / 業(yè)務(wù)層 / 數(shù)據(jù)層)的核心標(biāo)識(shí)之一。
核心核心特性:
- 專屬業(yè)務(wù)層標(biāo)識(shí):語義上明確標(biāo)注類的作用是業(yè)務(wù)邏輯處理(如業(yè)務(wù)規(guī)則判斷、數(shù)據(jù)層調(diào)用、事務(wù)處理等),替代通用的
@Component,讓代碼分層更清晰、可讀性更強(qiáng); - 自動(dòng)掃描注冊 Bean:與
@Component一致,Spring 啟動(dòng)時(shí)通過組件掃描,自動(dòng)將標(biāo)注該注解的類實(shí)例化并注冊到 IoC 容器,無需手動(dòng)配置 Bean; - 支持依賴注入:容器中的
@Service組件,可通過@Autowired/@Resource等注解,自動(dòng)注入依賴的其他 Bean(如@Repository數(shù)據(jù)層組件); - 天然支持事務(wù)管理:配合 Spring 聲明式事務(wù)
@Transactional注解,可直接在@Service方法上標(biāo)注,實(shí)現(xiàn)業(yè)務(wù)層的事務(wù)控制(核心適用場景)。
實(shí)戰(zhàn)代碼:
@Service
@RequiredArgsConstructor//Lombok注解,自動(dòng)生成構(gòu)造器,構(gòu)造器注入,避免字段注入
public class UserServiceImpl implements UserService {
private final UserMapper userMapper;
@Override
public User login(String username, String password) {
// 業(yè)務(wù)邏輯
User user = userMapper.selectByUsername(username);
if (user == null) {
throw new RuntimeException("用戶不存在");
}
if (!user.getPassword().equals(password)) {
throw new RuntimeException("密碼錯(cuò)誤");
}
return user;
}
}使用場景: 業(yè)務(wù)邏輯層
4.2 @Transactional
作用: @Transactional 是 Spring 框架實(shí)現(xiàn)聲明式事務(wù)管理的核心注解,基于 AOP(面向切面編程)實(shí)現(xiàn),無需手動(dòng)編寫 try-catch 事務(wù)提交 / 回滾代碼,僅通過注解標(biāo)注即可為方法 / 類開啟事務(wù)控制,保證一組數(shù)據(jù)庫操作的原子性(要么全部成功提交,要么任意一步失敗則整體回滾),是解決數(shù)據(jù)庫事務(wù)問題的標(biāo)準(zhǔn)方案。
核心注解屬性(常用配置)
@Transactional 提供多個(gè)屬性用于自定義事務(wù)行為,開發(fā)中可根據(jù)業(yè)務(wù)需求配置,核心常用屬性如下:
| 屬性名 | 作用 | 可選值示例、默認(rèn)值 |
|---|---|---|
readOnly | 設(shè)置事務(wù)是否為只讀(適用于僅查詢操作,提升數(shù)據(jù)庫性能) | true/false(默認(rèn)) |
isolation | 設(shè)置事務(wù)隔離級(jí)別(解決并發(fā)事務(wù)的臟讀 / 不可重復(fù)讀 / 幻讀問題) | READ_UNCOMMITTED/READ_COMMITTED/REPEATABLE_READ/SERIALIZABLE 數(shù)據(jù)庫默認(rèn)隔離級(jí)別(如 MySQL 為 REPEATABLE_READ) |
propagation | 設(shè)置事務(wù)傳播行為(解決方法嵌套調(diào)用時(shí),事務(wù)的創(chuàng)建 / 復(fù)用規(guī)則) | REQUIRED(最常用)/REQUIRES_NEW/SUPPORTS等 |
rollbackFor | 指定觸發(fā)回滾的異常類型(可包含 checked 異常) | Exception.class/BusinessException.class 默認(rèn):RuntimeException.class |
noRollbackFor | 指定不觸發(fā)回滾的異常類型 | IllegalArgumentException.class 默認(rèn):無 |
timeout | 設(shè)置事務(wù)超時(shí)時(shí)間(單位:秒),超時(shí)則自動(dòng)回滾 | 3/5 默認(rèn):-1(無超時(shí)限制) |
實(shí)戰(zhàn)代碼:
@Service
public class AnnouncementServiceImpl implements AnnouncementService {
@Transactional // 開啟事務(wù)
public void publishAnnouncement(Announcement announcement) {
// 1. 保存公告
announcementMapper.insert(announcement);
// 2. 記錄操作日志
logMapper.insert(new Log("發(fā)布公告", announcement.getId()));
// 如果第2步失敗,第1步會(huì)自動(dòng)回滾
}
}使用場景: 多個(gè)數(shù)據(jù)庫操作需要保證原子性
常見錯(cuò)誤:
// 錯(cuò)誤:@Transactional只對public方法有效
@Transactional
private void saveUser(User user) {
// 事務(wù)不生效
}
// 正確:必須是public方法
@Transactional
public void saveUser(User user) {
// 事務(wù)生效
}
五、MyBatis-Plus注解(7個(gè))
5.1 @TableName
作用:@TableName 是 MyBatis-Plus(MP)框架的核心表映射注解,專門用于解決Java 實(shí)體類與數(shù)據(jù)庫表之間的名稱映射問題,通過注解直接指定實(shí)體類對應(yīng)的數(shù)據(jù)庫表名,替代傳統(tǒng) MyBatis 中手動(dòng)編寫 SQL 映射或 XML 配置表名的操作,簡化 MP 的單表 CRUD 開發(fā),是 MP 實(shí)現(xiàn)零 SQL 單表操作的基礎(chǔ)注解之一。
核心設(shè)計(jì)背景
MyBatis-Plus 有默認(rèn)表名映射規(guī)則:實(shí)體類名(駝峰命名)自動(dòng)轉(zhuǎn)換為數(shù)據(jù)庫表名(下劃線命名)(如 Java 實(shí)體UserInfo → 數(shù)據(jù)庫表user_info)。
當(dāng)實(shí)體類名與數(shù)據(jù)庫表名不一致 / 不滿足駝峰轉(zhuǎn)下劃線規(guī)則時(shí)(如實(shí)體User對應(yīng)表t_user、實(shí)體Order對應(yīng)表order_info),默認(rèn)規(guī)則失效,需通過@TableName手動(dòng)指定映射關(guān)系,否則 MP 執(zhí)行 CRUD 時(shí)會(huì)因表名不存在拋出 SQL 異常。
實(shí)戰(zhàn)代碼:
@Data
@TableName("user") // 對應(yīng)數(shù)據(jù)庫的user表
public class User {
private Long userId;
private String username;
}
使用場景: 實(shí)體類名和表名不一致時(shí)
5.2 @TableId
作用: @TableId 是 MyBatis-Plus(MP)框架的專屬主鍵映射注解,核心作用是將Java 實(shí)體類的屬性與數(shù)據(jù)庫表的主鍵字段建立綁定關(guān)系,同時(shí)指定主鍵的生成策略(如自增、雪花算法、UUID 等),是 MP 實(shí)現(xiàn)單表 CRUD 時(shí)主鍵自動(dòng)處理的核心注解,替代傳統(tǒng) MyBatis 手動(dòng)配置主鍵映射和生成邏輯的操作。
核心設(shè)計(jì)背景
MyBatis-Plus 默認(rèn)會(huì)將實(shí)體類中名為id的屬性映射為數(shù)據(jù)庫表的主鍵字段,且默認(rèn)使用雪花算法(IdType.ASSIGN_ID) 生成主鍵值。
當(dāng)實(shí)體主鍵屬性名與表主鍵字段名不一致(如實(shí)體userId對應(yīng)表user_id)、需要自定義主鍵生成策略(如數(shù)據(jù)庫自增、UUID)時(shí),默認(rèn)規(guī)則失效,需通過@TableId顯式配置,否則 MP 執(zhí)行新增 / 查詢等操作時(shí)會(huì)因主鍵映射錯(cuò)誤 / 生成規(guī)則不符拋出異常。
實(shí)戰(zhàn)代碼:
@Data
@TableName("user")
public class User {
@TableId(type = IdType.AUTO) // 主鍵自增
private Long userId;
}
主鍵策略:
| 策略 | 說明 |
|---|---|
IdType.AUTO | 數(shù)據(jù)庫自增 |
IdType.ASSIGN_ID | 雪花算法生成ID(默認(rèn)) |
IdType.INPUT | 手動(dòng)輸入 |
5.3 @TableField
作用: @TableField 是 MyBatis-Plus(MP)框架的普通字段專屬映射注解,核心作用是將Java 實(shí)體類的非主鍵屬性與數(shù)據(jù)庫表的普通字段建立綁定關(guān)系,同時(shí)支持配置字段是否參與 CRUD、是否為查詢條件、字段填充規(guī)則等特性,是 MP 解決非主鍵字段映射不一致、實(shí)現(xiàn)字段精細(xì)化控制的核心注解,替代傳統(tǒng) MyBatis 手動(dòng)在 XML/SQL 中配置字段映射的操作。
核心設(shè)計(jì)背景
MyBatis-Plus 有默認(rèn)普通字段映射規(guī)則:實(shí)體類屬性名(駝峰命名)自動(dòng)轉(zhuǎn)換為數(shù)據(jù)庫表字段名(下劃線命名)(如實(shí)體userName → 表user_name)。
當(dāng)實(shí)體屬性名與表字段名不一致 / 不滿足駝峰轉(zhuǎn)下劃線規(guī)則、需要排除某些字段不參與 MP 的自動(dòng) CRUD、需要配置字段自動(dòng)填充(如創(chuàng)建時(shí)間、更新時(shí)間)時(shí),默認(rèn)規(guī)則無法滿足需求,需通過@TableField顯式配置,否則 MP 執(zhí)行操作時(shí)會(huì)因字段映射錯(cuò)誤拋出 SQL 異常,或無法實(shí)現(xiàn)字段的精細(xì)化控制。
實(shí)戰(zhàn)代碼:
@Data
@TableName("user")
public class User {
@TableId(type = IdType.AUTO)
private Long userId;
// 自動(dòng)填充:插入時(shí)填充
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
// 自動(dòng)填充:插入和更新時(shí)都填充
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}自動(dòng)填充處理器:
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
}5.4 @Version
作用: @Version 是 MyBatis-Plus(MP)框架實(shí)現(xiàn)樂觀鎖機(jī)制的專屬核心注解,核心作用是將Java 實(shí)體類的屬性與數(shù)據(jù)庫表的版本數(shù)字段綁定,讓 MP 自動(dòng)基于該字段實(shí)現(xiàn)樂觀鎖的版本控制,解決多線程 / 多用戶并發(fā)更新數(shù)據(jù)時(shí)的臟寫問題(避免多個(gè)請求同時(shí)修改同一條數(shù)據(jù),導(dǎo)致數(shù)據(jù)覆蓋、一致性丟失),是 MP 簡化樂觀鎖開發(fā)的關(guān)鍵注解。
實(shí)戰(zhàn)代碼:
@Data
@TableName("user")
public class User {
@TableId(type = IdType.AUTO)
private Long userId;
@Version // 樂觀鎖
private Integer version;
}工作原理:
-- 更新時(shí)自動(dòng)加上version條件 UPDATE user SET username = ?, version = version + 1 WHERE user_id = ? AND version = ? -- 如果version不匹配(被其他線程修改了),更新失敗
使用場景: 防止并發(fā)修改沖突
5.5 @TableLogic
作用: @TableLogic 是 MyBatis-Plus(MP)框架實(shí)現(xiàn)邏輯刪除的專屬核心注解,核心作用是將 Java 實(shí)體類的屬性與數(shù)據(jù)庫表的邏輯刪除標(biāo)識(shí)字段綁定,讓 MP 自動(dòng)將單表 CRUD 中的物理刪除(DELETE)操作替換為邏輯刪除(UPDATE)操作,同時(shí)自動(dòng)過濾查詢 / 更新結(jié)果中已邏輯刪除的數(shù)據(jù),實(shí)現(xiàn)數(shù)據(jù)的「假刪除、軟刪除」,避免真實(shí)刪除數(shù)據(jù)導(dǎo)致的歷史數(shù)據(jù)丟失、關(guān)聯(lián)查詢異常問題,是 MP 簡化邏輯刪除開發(fā)的關(guān)鍵注解。
實(shí)戰(zhàn)代碼:
@Data
@TableName("user")
public class User {
@TableId(type = IdType.AUTO)
private Long userId;
@TableLogic // 邏輯刪除:0=正常,1=已刪除
private Integer deleted;
}效果:
// 調(diào)用刪除方法 userMapper.deleteById(1); // 實(shí)際執(zhí)行的SQL(不是真刪除) UPDATE user SET deleted = 1 WHERE user_id = 1 AND deleted = 0 // 查詢時(shí)自動(dòng)過濾已刪除的數(shù)據(jù) SELECT * FROM user WHERE deleted = 0
5.6 @MapperScan
作用: @MapperScan 是 MyBatis 及 MyBatis-Plus 框架的核心 Mapper 接口掃描注解,核心作用是指定項(xiàng)目中 Mapper 接口的掃描包路徑,讓框架自動(dòng)掃描該包下所有的 Mapper 接口,并為每個(gè)接口動(dòng)態(tài)生成代理實(shí)現(xiàn)類,最終將這些代理類注冊到 Spring IoC 容器中,實(shí)現(xiàn) Mapper 接口的依賴注入和數(shù)據(jù)庫操作調(diào)用,是整合 MyBatis/MP 與 SpringBoot 的關(guān)鍵注解。
實(shí)戰(zhàn)代碼:
@MapperScan("com.liu.mapper") // 掃描mapper包下的所有接口
@SpringBootApplication
public class SimpleAccountingApplication {
public static void main(String[] args) {
SpringApplication.run(SimpleAccountingApplication.class, args);
}
}使用場景: 啟動(dòng)類上,掃描所有Mapper接口
5.7 @Select / @Insert / @Update / @Delete
作用: @Select、@Insert、@Update、@Delete 是 MyBatis/MyBatis-Plus(MP)框架的核心原生 SQL 注解,屬于 MyBatis 基礎(chǔ)注解(MP 完全兼容),核心作用是直接在 Mapper 接口的方法上標(biāo)注對應(yīng)的 SQL 語句,讓框架將接口方法與 SQL 語句直接綁定,調(diào)用接口方法時(shí)自動(dòng)執(zhí)行標(biāo)注的 SQL,實(shí)現(xiàn)數(shù)據(jù)庫的查詢、新增、更新、刪除操作。
這組注解替代了傳統(tǒng) MyBatis 中XML 映射文件的編寫(如UserMapper.xml中的<select>/<insert>標(biāo)簽),實(shí)現(xiàn)「SQL 與 Mapper 接口方法一體化」,簡化單表 / 多表的 SQL 開發(fā),是 MyBatis/MP 中除了 MP 自動(dòng) CRUD 外,自定義 SQL 的核心實(shí)現(xiàn)方式。
實(shí)戰(zhàn)代碼:
@Mapper
public interface UserMapper extends BaseMapper<User> {
@Select("SELECT * FROM user WHERE username = #{username}")
User selectByUsername(String username);
@Update("UPDATE user SET status = #{status} WHERE user_id = #{userId}")
int updateStatus(@Param("userId") Long userId, @Param("status") Integer status);
}1. 參數(shù)綁定規(guī)范
- 優(yōu)先使用
#{參數(shù)名}:實(shí)現(xiàn)預(yù)編譯參數(shù)綁定,MyBatis 會(huì)自動(dòng)處理參數(shù)類型轉(zhuǎn)換,有效防止 SQL 注入,是開發(fā)中的標(biāo)準(zhǔn)用法; - 慎用
${參數(shù)名}:直接將參數(shù)拼接到 SQL 中,無預(yù)編譯處理,有 SQL 注入風(fēng)險(xiǎn),僅適用于表名、字段名動(dòng)態(tài)拼接(如SELECT * FROM ${tableName}); - 多參數(shù)綁定:若方法有多個(gè)獨(dú)立入?yún)?,可通過**@Param(“參數(shù)名”)**指定參數(shù)名,保證#{}中名稱匹配:
// 多參數(shù)需標(biāo)注@Param
@Select("SELECT * FROM t_user WHERE username = #{name} AND age = #{age}")
User selectByUsernameAndAge(@Param("name") String username, @Param("age") Integer age);
使用場景: 簡單SQL可以用注解,復(fù)雜SQL建議用XML
六、AOP注解(3個(gè))
6.1 @Aspect
作用:@Aspect 是 Spring 框架實(shí)現(xiàn) AOP(面向切面編程)的核心注解,核心作用是標(biāo)記一個(gè)普通 Java 類為「切面類(Aspect)」,讓 Spring 識(shí)別該類為 AOP 的核心載體 —— 用于封裝切入點(diǎn)(Pointcut) 和通知(Advice) 邏輯,實(shí)現(xiàn)業(yè)務(wù)代碼與非業(yè)務(wù)橫切邏輯(如日志記錄、事務(wù)控制、權(quán)限校驗(yàn)、性能監(jiān)控)的解耦,是 Spring AOP 開發(fā)的基礎(chǔ)注解。
實(shí)戰(zhàn)代碼:
@Aspect
@Component
@Slf4j
public class RoleCheckAspect {
@Before("@annotation(requireRole)")//在切點(diǎn)之前運(yùn)行的代碼
public void checkRole(JoinPoint joinPoint, RequireRole requireRole) {
// 權(quán)限驗(yàn)證邏輯
int[] allowedRoles = requireRole.value();
int userRole = TokenHelper.getRole(request);
boolean hasPermission = Arrays.stream(allowedRoles)
.anyMatch(role -> role == userRole);
if (!hasPermission) {
throw new RuntimeException("無權(quán)限訪問");
}
}
}@Aspect 關(guān)鍵使用注意事項(xiàng)(避坑重點(diǎn))
- 切面類必須標(biāo)注 @Component
@Aspect僅標(biāo)記該類為切面,不會(huì)自動(dòng)將其注冊到 Spring IoC 容器,必須配合@Component(或 @Service/@Repository),讓 Spring 掃描并管理該 Bean,否則 AOP 邏輯完全失效。
- @Around 通知的核心要求
@Around是唯一能控制目標(biāo)方法執(zhí)行的通知,必須遵守 3 個(gè)規(guī)則,否則會(huì)導(dǎo)致業(yè)務(wù)異常:
- 必須調(diào)用
joinPoint.proceed():執(zhí)行目標(biāo)業(yè)務(wù)方法,省略則目標(biāo)方法不會(huì)執(zhí)行; - 必須返回
proceed()的結(jié)果:否則目標(biāo)方法的返回值會(huì)丟失(前端獲取不到數(shù)據(jù)); - 異常必須重新拋出:若捕獲異常,需手動(dòng)拋出,否則后續(xù)異常處理器(如 @RestControllerAdvice)無法感知。
- 切入點(diǎn)表達(dá)式需精準(zhǔn)
- 避免寫過于寬泛的表達(dá)式(如
execution(* *..*.*(..))),會(huì)匹配所有方法,導(dǎo)致性能損耗; - 表達(dá)式中的包名、類名需與實(shí)際一致,否則無法匹配目標(biāo)方法,切面邏輯不生效。
- Spring AOP 的代理限制
Spring AOP 默認(rèn)基于動(dòng)態(tài)代理實(shí)現(xiàn),存在 2 個(gè)核心限制,會(huì)導(dǎo)致切面邏輯不生效:
? 4.1 僅對 Spring 容器中的 Bean 生效
非 Spring 管理的類(未標(biāo)注 @Component/@Service 等),其方法無法被切面增強(qiáng),解決方案:將類注冊為 Spring Bean。
? 4.2 僅對公共(public)方法生效
private/protected/default 訪問權(quán)限的方法,Spring AOP 不會(huì)生成代理,切面邏輯無法織入,解決方案:將方法改為 public(若需增強(qiáng)非公共方法,需使用 AspectJ 原生織入,而非 Spring AOP)。
- 內(nèi)部方法自調(diào)用會(huì)導(dǎo)致切面失效
同一類中,無切面增強(qiáng)的方法調(diào)用有切面增強(qiáng)的方法,因未經(jīng)過 Spring 代理對象,切面邏輯無法織入,這是 Spring AOP 最常見的坑:
@Service
public class UserService {
// 無切面增強(qiáng)的方法
public void test() {
this.saveUser(); // 內(nèi)部自調(diào)用,saveUser的切面邏輯失效
}
// 有切面增強(qiáng)的方法(如@Transactional/@Around)
public void saveUser() {
// 業(yè)務(wù)邏輯
}
}解決方案:
- 將方法拆分到不同的 Spring Bean 中,通過依賴注入調(diào)用;
- 注入自身的代理對象,通過代理對象調(diào)用(需開啟暴露代理);
- 使用
AopContext.currentProxy()獲取代理對象調(diào)用。
使用場景: 權(quán)限驗(yàn)證、日志記錄、性能監(jiān)控
6.2 @Before / @After / @Around
作用: @Before、@After、@Around 是 Spring AOP 中最核心的三大通知注解,與 @Aspect 切面類配合使用,核心作用是定義橫切邏輯的織入時(shí)機(jī)—— 將日志記錄、性能監(jiān)控、權(quán)限校驗(yàn)等通用非業(yè)務(wù)邏輯,精準(zhǔn)織入到目標(biāo)方法的執(zhí)行前、執(zhí)行后、執(zhí)行前后全生命周期,實(shí)現(xiàn)業(yè)務(wù)代碼與橫切邏輯的解耦,是 Spring AOP 實(shí)現(xiàn)「無侵入式功能增強(qiáng)」的核心載體。
三者均標(biāo)注在切面類(@Aspect + @Component 標(biāo)注)的方法上,需綁定切入點(diǎn)(@Pointcut) 指定增強(qiáng)的目標(biāo)方法,共同覆蓋目標(biāo)方法的核心執(zhí)行節(jié)點(diǎn),其中 @Around 功能最全面,可實(shí)現(xiàn)前兩者的所有效果。
三大注解核心定義與核心作用:
1. @Before:前置通知注解
核心定義:標(biāo)注在切面類方法上,指定橫切邏輯在目標(biāo)方法執(zhí)行前執(zhí)行,是最基礎(chǔ)的通知類型。
核心作用:用于實(shí)現(xiàn)目標(biāo)方法執(zhí)行前的預(yù)處理邏輯,無返回值,無法修改目標(biāo)方法的入?yún)?,也無法控制目標(biāo)方法是否執(zhí)行。
核心適用場景:權(quán)限校驗(yàn)、接口入?yún)⑷罩居涗洝①Y源初始化(如數(shù)據(jù)庫連接準(zhǔn)備)、請求參數(shù)預(yù)處理等。
2. @After:最終通知注解
核心定義:標(biāo)注在切面類方法上,指定橫切邏輯在目標(biāo)方法執(zhí)行結(jié)束后執(zhí)行(無論目標(biāo)方法是否拋出異常),是「必執(zhí)行」的通知類型。
核心作用:用于實(shí)現(xiàn)目標(biāo)方法執(zhí)行后的收尾邏輯,無返回值,無法獲取目標(biāo)方法返回值或異常信息,僅做最終兜底處理。
核心適用場景:資源統(tǒng)一釋放(如關(guān)閉數(shù)據(jù)庫連接、清空臨時(shí)緩存、關(guān)閉文件流)、方法執(zhí)行完成的通用日志標(biāo)記等。
3. @Around:環(huán)繞通知注解
核心定義:標(biāo)注在切面類方法上,指定橫切邏輯包裹目標(biāo)方法的全執(zhí)行生命周期(執(zhí)行前 + 執(zhí)行中 + 執(zhí)行后 + 異常時(shí)),是功能最強(qiáng)大、最靈活的通知類型。
核心作用:可實(shí)現(xiàn)目標(biāo)方法的全流程控制:能修改入?yún)ⅰ⑹謩?dòng)控制目標(biāo)方法是否執(zhí)行、獲取 / 修改返回值、捕獲處理異常,完全覆蓋 @Before 和 @After 的能力。
核心適用場景:性能監(jiān)控(統(tǒng)計(jì)方法執(zhí)行耗時(shí))、全流程日志記錄(入?yún)?+ 執(zhí)行結(jié)果 + 耗時(shí))、核心接口的權(quán)限控制、方法返回值緩存等。
核心使用差異
| 注解 | 織入時(shí)機(jī) | 核心能力 | 方法入?yún)?/th> | 關(guān)鍵特性 | 核心限制 |
|---|---|---|---|---|---|
| @Before | 目標(biāo)方法執(zhí)行前 | 僅做前置預(yù)處理,無返回值 | JoinPoint(獲取方法信息) | 執(zhí)行時(shí)機(jī)固定,邏輯簡單 | 無法修改入?yún)?、無法控制目標(biāo)方法執(zhí)行、無返回值 |
| @After | 目標(biāo)方法執(zhí)行結(jié)束后(必執(zhí)行) | 僅做最終收尾處理,無返回值 | JoinPoint(獲取方法信息) | 無論異常與否都會(huì)執(zhí)行 | 無法獲取返回值、無法捕獲異常、無返回值 |
| @Around | 目標(biāo)方法執(zhí)行前后全生命周期 | 修改入?yún)?、控制?zhí)行、獲取 / 修改返回值、捕獲異常,覆蓋前兩者所有能力 | ProceedingJoinPoint(含執(zhí)行方法) | 功能最全面,靈活性最高 | 必須調(diào)用 proceed() 執(zhí)行目標(biāo)方法,必須返回結(jié)果 |
關(guān)鍵入?yún)⒄f明
- JoinPoint:基礎(chǔ)入?yún)?,可獲取目標(biāo)方法的類名、方法名、入?yún)?shù)組等信息,
@Before/@After可直接使用; - ProceedingJoinPoint:繼承自
JoinPoint,新增proceed()方法 ——手動(dòng)執(zhí)行目標(biāo)業(yè)務(wù)方法,是@Around的專屬入?yún)?,無此方法則無法觸發(fā)目標(biāo)方法執(zhí)行。
完整使用示例(同一切面,對比實(shí)現(xiàn))
以業(yè)務(wù)層方法(@Service) 為增強(qiáng)目標(biāo),實(shí)現(xiàn)「日志記錄 + 性能監(jiān)控」功能,分別演示三者的使用方式,統(tǒng)一綁定同一個(gè)切入點(diǎn),便于對比差異。
環(huán)境前提
- 引入 Spring AOP 起步依賴(Spring Boot 自動(dòng)配置):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>- 目標(biāo)業(yè)務(wù)類(被增強(qiáng)的方法,無任何侵入式修改):
import org.springframework.stereotype.Service;
@Service
public class UserService {
// 正常方法:返回結(jié)果
public String getUserById(Long id) throws InterruptedException {
Thread.sleep(80); // 模擬業(yè)務(wù)執(zhí)行耗時(shí)
return "查詢到用戶ID:" + id;
}
// 異常方法:拋出運(yùn)行時(shí)異常
public void deleteUserById(Long id) {
throw new RuntimeException("模擬刪除失?。河脩舨淮嬖?);
}
}切面類實(shí)現(xiàn)(@Aspect + @Component)
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.Arrays;
// 1. 標(biāo)記為切面類 2. 注冊為Spring Bean(必須,否則AOP失效)
@Aspect
@Component
public class AopAdviceDemoAspect {
private static final Logger log = LoggerFactory.getLogger(AopAdviceDemoAspect.class);
// 定義公共切入點(diǎn):匹配UserService類的所有公共方法(精準(zhǔn)增強(qiáng)目標(biāo))
@Pointcut("execution(public * com.example.demo.service.UserService.*(..))")
public void userServicePointcut() {
// 切入點(diǎn)方法:僅做標(biāo)識(shí),無需編寫任何邏輯
}
// ==================== 1. @Before 前置通知 ====================
@Before("userServicePointcut()")
public void doBefore(JoinPoint joinPoint) {
// 獲取方法信息:類名.方法名
String methodFullName = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
// 獲取方法入?yún)?
Object[] args = joinPoint.getArgs();
// 前置邏輯:記錄方法開始執(zhí)行 + 入?yún)?
log.info("【@Before 前置通知】方法{}開始執(zhí)行,入?yún)ⅲ簕}", methodFullName, Arrays.toString(args));
}
// ==================== 2. @After 最終通知 ====================
@After("userServicePointcut()")
public void doAfter(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
// 最終邏輯:標(biāo)記方法執(zhí)行結(jié)束(無論是否異常,都會(huì)打?。?
log.info("【@After 最終通知】方法{}執(zhí)行結(jié)束(必執(zhí)行)", methodName);
}
// ==================== 3. @Around 環(huán)繞通知 ====================
@Around("userServicePointcut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
// ① 執(zhí)行前邏輯:記錄開始時(shí)間 + 獲取方法信息(等效@Before)
long startTime = System.currentTimeMillis();
String methodFullName = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
log.info("【@Around 執(zhí)行前】方法{}入?yún)ⅲ簕}", methodFullName, Arrays.toString(args));
Object result = null;
try {
// ② 手動(dòng)執(zhí)行目標(biāo)方法(核心:必須調(diào)用,否則目標(biāo)方法不執(zhí)行)
result = joinPoint.proceed();
// ③ 執(zhí)行成功后邏輯:記錄返回值 + 耗時(shí)
long costTime = System.currentTimeMillis() - startTime;
log.info("【@Around 執(zhí)行成功】方法{}返回值:{},耗時(shí):{}ms", methodFullName, result, costTime);
} catch (Throwable e) {
// ④ 執(zhí)行異常時(shí)邏輯:記錄異常信息 + 耗時(shí)
long costTime = System.currentTimeMillis() - startTime;
log.error("【@Around 執(zhí)行異?!糠椒▄}執(zhí)行失敗,耗時(shí):{}ms,異常:{}", methodFullName, costTime, e.getMessage());
throw e; // 重新拋出異常,讓全局異常處理器處理(不可省略)
}
// ⑤ 返回目標(biāo)方法結(jié)果(核心:必須返回,否則調(diào)用方獲取不到返回值)
return result;
}
}實(shí)戰(zhàn)代碼:
@Aspect
@Component
public class LogAspect {
// 前置通知:方法執(zhí)行前
@Before("execution(* com.liu.controller.*.*(..))")
public void before(JoinPoint joinPoint) {
log.info("方法執(zhí)行前:{}", joinPoint.getSignature().getName());
}
// 后置通知:方法執(zhí)行后
@After("execution(* com.liu.controller.*.*(..))")
public void after(JoinPoint joinPoint) {
log.info("方法執(zhí)行后:{}", joinPoint.getSignature().getName());
}
// 環(huán)繞通知:方法執(zhí)行前后
@Around("execution(* com.liu.controller.*.*(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed(); // 執(zhí)行方法
long end = System.currentTimeMillis();
log.info("方法執(zhí)行耗時(shí):{}ms", end - start);
return result;
}
}6.3 自定義注解
作用:自定義注解是 Java 提供的元編程能力,允許開發(fā)者根據(jù)業(yè)務(wù)需求定義專屬的注解類型,結(jié)合 Spring 框架的 AOP、反射等特性,可實(shí)現(xiàn)無侵入式的業(yè)務(wù)增強(qiáng)(如自定義權(quán)限校驗(yàn)、操作日志、接口限流等),是企業(yè)級(jí)開發(fā)中實(shí)現(xiàn)通用邏輯復(fù)用、簡化業(yè)務(wù)代碼的核心手段。
自定義注解的核心價(jià)值是將通用邏輯與業(yè)務(wù)代碼解耦:通過注解標(biāo)記需要增強(qiáng)的類 / 方法 / 屬性,再通過注解解析器(反射 / AOP)識(shí)別注解并執(zhí)行對應(yīng)的橫切邏輯,無需修改業(yè)務(wù)代碼即可實(shí)現(xiàn)功能增強(qiáng)。
實(shí)戰(zhàn)代碼:
// 1. 定義注解
@Target(ElementType.METHOD) // 作用在方法上
@Retention(RetentionPolicy.RUNTIME) // 運(yùn)行時(shí)有效
public @interface RequireRole {
int[] value() default {}; // 允許的角色
}
// 2. 使用注解
@RestController
@RequestMapping("/admin")
public class AdminController {
@RequireRole({Constants.ROLE_ADMIN}) // 只允許管理員訪問
@GetMapping("/users")
public Result<List<User>> getAllUsers() {
return Result.success(userService.list());
}
}
// 3. 切面處理注解
@Aspect
@Component
public class RoleCheckAspect {
@Before("@annotation(requireRole)")
public void checkRole(JoinPoint joinPoint, RequireRole requireRole) {
// 驗(yàn)證權(quán)限
}
}使用場景: 權(quán)限驗(yàn)證、日志記錄、緩存控制
七、Lombok注解(5個(gè))
首先先引入依賴:
<!-- Maven 依賴(與其他 Lombok 注解共用) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>7.1 @Data
作用: @Data 是 Lombok 框架的核心注解,核心作用是自動(dòng)為 Java 類生成通用的模板代碼,包括所有成員變量的getter/setter方法、toString()、equals()、hashCode()方法,以及全參 / 無參構(gòu)造器的核心部分,徹底消除實(shí)體類、POJO 類中大量重復(fù)的模板代碼,大幅簡化類的編寫,提升開發(fā)效率并減少代碼維護(hù)成本。
實(shí)戰(zhàn)代碼:
@Data
public class User {
private Long userId;
private String username;
private String password;
}
// 等價(jià)于手寫:
public class User {
private Long userId;
private String username;
private String password;
public Long getUserId() { return userId; }
public void setUserId(Long userId) { this.userId = userId; }
// ... 其他getter/setter
@Override
public String toString() { ... }
@Override
public boolean equals(Object o) { ... }
@Override
public int hashCode() { ... }
}7.2 @NoArgsConstructor / @AllArgsConstructor
作用: @NoArgsConstructor 和 @AllArgsConstructor 是 Lombok 框架的核心基礎(chǔ)注解,二者配合使用可分別為 Java 類自動(dòng)生成無參構(gòu)造器和全參構(gòu)造器,徹底消除手動(dòng)編寫構(gòu)造器的模板代碼,簡化對象實(shí)例化操作,常與 @Data 組合成為實(shí)體類、POJO 類的標(biāo)配注解。
構(gòu)造器是 Java 類實(shí)例化對象的核心入口,手動(dòng)編寫多參構(gòu)造器不僅繁瑣,還會(huì)在新增 / 刪除字段時(shí)需同步修改,而這兩個(gè)注解通過編譯期字節(jié)碼增強(qiáng)自動(dòng)生成構(gòu)造器,開發(fā)時(shí)僅需注解標(biāo)注,編譯后字節(jié)碼中會(huì)包含對應(yīng)構(gòu)造器,與手動(dòng)編寫完全一致且運(yùn)行時(shí)無性能損耗。
實(shí)戰(zhàn)代碼:
@Data
@NoArgsConstructor // 無參構(gòu)造器
@AllArgsConstructor // 全參構(gòu)造器
public class User {
private Long userId;
private String username;
}
// 等價(jià)于:
public class User {
public User() {} // 無參構(gòu)造器
public User(Long userId, String username) { // 全參構(gòu)造器
this.userId = userId;
this.username = username;
}
}7.3 @RequiredArgsConstructor
作用: @RequiredArgsConstructor 是 Lombok 框架的核心構(gòu)造器注解,核心作用是為 Java 類自動(dòng)生成包含「必須初始化成員變量」的構(gòu)造器,這里的「必須初始化變量」特指被 final 修飾的成員變量和被 @NonNull 注解標(biāo)注的成員變量(這兩類變量在 Java 語法中要求必須初始化,否則編譯報(bào)錯(cuò))。該注解精準(zhǔn)解決了「僅為關(guān)鍵變量生成帶參構(gòu)造器」的需求,避免全參構(gòu)造器參數(shù)冗余,常與 @Data、@NoArgsConstructor 等注解組合使用,是實(shí)體類、配置類、服務(wù)類開發(fā)中的常用注解。
實(shí)戰(zhàn)代碼:
@Service
@RequiredArgsConstructor // 自動(dòng)生成構(gòu)造器
public class UserServiceImpl implements UserService {
private final UserMapper userMapper; // final修飾,依賴注入
private final JwtUtil jwtUtil;
// Lombok自動(dòng)生成:
// public UserServiceImpl(UserMapper userMapper, JwtUtil jwtUtil) {
// this.userMapper = userMapper;
// this.jwtUtil = jwtUtil;
// }
}為什么推薦?
- 代碼簡潔
- 不可變(final)
- Spring自動(dòng)注入
7.4 @Slf4j
作用: @Slf4j 是 Lombok 框架的核心日志注解,核心作用是為 Java 類自動(dòng)生成基于 SLF4J 規(guī)范的日志對象(private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(當(dāng)前類.class);),徹底消除手動(dòng)聲明日志對象的模板代碼,直接通過 log.info()/log.error() 等方法打印日志,是企業(yè)級(jí) Java 開發(fā)中日志記錄的標(biāo)配注解,與 @Data、@Service、@RestController 等注解無縫配合使用。
實(shí)戰(zhàn)代碼:
@Service
@Slf4j // 自動(dòng)生成log對象
public class UserServiceImpl implements UserService {
public User login(String username, String password) {
log.info("用戶登錄:{}", username); // 直接使用log
log.error("登錄失敗:{}", username);
log.debug("調(diào)試信息:{}", username);
}
}
// 等價(jià)于:
public class UserServiceImpl implements UserService {
private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);
}7.5 @Builder
作用: @Builder 是 Lombok 框架的核心創(chuàng)建型模式注解,核心作用是為 Java 類自動(dòng)實(shí)現(xiàn)建造者(Builder)設(shè)計(jì)模式,通過鏈?zhǔn)秸{(diào)用的方式靈活創(chuàng)建對象,替代傳統(tǒng)的無參構(gòu)造 + 多次 setter、多參構(gòu)造器的對象實(shí)例化方式,解決多字段對象創(chuàng)建時(shí)代碼繁瑣、參數(shù)順序易混淆、可選參數(shù)處理麻煩的問題,是企業(yè)級(jí)開發(fā)中創(chuàng)建復(fù)雜對象的標(biāo)配注解,常與 @Data、@NoArgsConstructor、@AllArgsConstructor 組合使用。
實(shí)戰(zhàn)代碼:
@Data
@Builder
public class User {
private Long userId;
private String username;
private String password;
}
// 使用:
User user = User.builder()
.userId(1L)
.username("admin")
.password("123456")
.build();使用場景: 構(gòu)建復(fù)雜對象
八、注解組合速查表
8.1 控制器常用組合
@RestController
@RequestMapping("/user")
@RequiredArgsConstructor
@Slf4j
public class UserController {
private final UserService userService;
@PostMapping("/login")
public Result<String> login(@RequestParam String username,
@RequestParam String password) {
log.info("用戶登錄:{}", username);
return userService.login(username, password);
}
}8.2 實(shí)體類常用組合
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("user")
public class User {
@TableId(type = IdType.AUTO)
private Long userId;
@NotBlank(message = "用戶名不能為空")
private String username;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@Version
private Integer version;
@TableLogic
private Integer deleted;
}8.3 Service層常用組合
@Service
@RequiredArgsConstructor
@Slf4j
public class UserServiceImpl implements UserService {
private final UserMapper userMapper;
@Transactional
public void saveUser(User user) {
log.info("保存用戶:{}", user.getUsername());
userMapper.insert(user);
}
}九、常見錯(cuò)誤匯總
錯(cuò)誤1:忘記加@Component系列注解
// 錯(cuò)誤:沒有@Service注解,Spring不會(huì)管理這個(gè)類
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper; // 注入失敗,userMapper為null
}
// 正確:加上@Service
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper; // 注入成功
}錯(cuò)誤2:@Autowired注入失敗
// 錯(cuò)誤:UserMapper沒有被Spring管理
public interface UserMapper { // 缺少@Mapper注解
User selectById(Long id);
}
// 正確:加上@Mapper或在啟動(dòng)類加@MapperScan
@Mapper
public interface UserMapper {
User selectById(Long id);
}錯(cuò)誤3:@Transactional不生效
// 錯(cuò)誤:private方法,事務(wù)不生效
@Transactional
private void saveUser(User user) {
userMapper.insert(user);
}
// 正確:必須是public方法
@Transactional
public void saveUser(User user) {
userMapper.insert(user);
}錯(cuò)誤4:@RequestBody和@RequestParam混用
// 錯(cuò)誤:不能同時(shí)使用
@PostMapping("/add")
public Result<Boolean> add(@RequestBody Bill bill,
@RequestParam Long userId) {
// 報(bào)錯(cuò)
}
// 正確:要么全用@RequestBody,要么全用@RequestParam
@PostMapping("/add")
public Result<Boolean> add(@RequestBody Map<String, Object> params) {
Long userId = (Long) params.get("userId");
Bill bill = (Bill) params.get("bill");
}十、總結(jié)
10.1 必會(huì)注解(按使用頻率排序)
| 排名 | 注解 | 使用頻率 | 重要程度 |
|---|---|---|---|
| 1 | @RestController | ????? | ????? |
| 2 | @RequestMapping | ????? | ????? |
| 3 | @Autowired | ????? | ????? |
| 4 | @Service | ????? | ????? |
| 5 | @Data | ????? | ???? |
| 6 | @RequestParam | ???? | ???? |
| 7 | @RequestBody | ???? | ???? |
| 8 | @PathVariable | ??? | ??? |
| 9 | @Transactional | ??? | ????? |
| 10 | @Valid | ??? | ???? |
10.2 快速查詢表
需要返回JSON? → @RestController
需要獲取URL參數(shù)? → @RequestParam
需要獲取JSON數(shù)據(jù)? → @RequestBody
需要獲取路徑參數(shù)? → @PathVariable
需要參數(shù)校驗(yàn)? → @Valid + 校驗(yàn)注解
需要依賴注入? → @Autowired 或 @RequiredArgsConstructor
需要事務(wù)? → @Transactional
需要日志? → @Slf4j
需要權(quán)限驗(yàn)證? → 自定義注解 + @Aspect
寫在最后
這篇文章整理了我在開發(fā)記賬系統(tǒng)時(shí)用到的所有注解,每個(gè)示例都來自真實(shí)項(xiàng)目代碼。
到此這篇關(guān)于Spring Boot常用注解速查表(30個(gè)必會(huì)注解+實(shí)戰(zhàn)案例)的文章就介紹到這了,更多相關(guān)Spring Boot注解速查表內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot獲取前臺(tái)參數(shù)的六種方式以及統(tǒng)一響應(yīng)
本文主要介紹了SpringBoot獲取前臺(tái)參數(shù)的六種方式以及統(tǒng)一響應(yīng),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03
java 枚舉類定義靜態(tài)valueOf(java.lang.String)方法的問題及解決
這篇文章主要介紹了java 枚舉類定義靜態(tài)valueOf(java.lang.String)方法的問題及解決,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09
基于JavaSwing+mysql開發(fā)一個(gè)學(xué)生社團(tuán)管理系統(tǒng)設(shè)計(jì)和實(shí)現(xiàn)
項(xiàng)目使用Java swing+mysql開發(fā),可實(shí)現(xiàn)基礎(chǔ)數(shù)據(jù)維護(hù)、用戶登錄注冊、社團(tuán)信息列表查看、社團(tuán)信息添加、社團(tuán)信息修改、社團(tuán)信息刪除以及退出注銷等功能、界面設(shè)計(jì)比較簡單易學(xué)、適合作為Java課設(shè)設(shè)計(jì)以及學(xué)習(xí)技術(shù)使用,需要的朋友參考下吧2021-08-08
SpringSecurity中的UserDetailsService深度解析
文章介紹了Spring Security中的UserDetailsService接口,它用于自定義用戶認(rèn)證邏輯,通過實(shí)現(xiàn)該接口,可以自定義用戶認(rèn)證過程,文章詳細(xì)解釋了UserDetailsService接口的返回值、方法參數(shù)以及可能出現(xiàn)的異常,如UsernameNotFoundException,感興趣的朋友跟隨小編一起看看吧2025-11-11
springboot+jsonp解決前端跨域問題小結(jié)
這篇文章主要介紹了springboot+jsonp解決前端跨域問題小結(jié),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-06-06
從實(shí)戰(zhàn)角度詳解Disruptor高性能隊(duì)列
這篇文章主要介紹了從實(shí)戰(zhàn)角度詳解Disruptor高性能隊(duì)列,對正在學(xué)習(xí)這方面知識(shí)的小伙伴有很大的幫助,感興趣的小伙伴快來一起學(xué)習(xí)吧2021-08-08

