SpringBoot整合Aop全過程
AOP 可以做什么?
- 1. 日志記錄
- 自動記錄方法的調用、參數、返回值和異常等信息,減少了在每個方法中添加日志代碼的需要。
- 2. 管理事務
- 在方法執(zhí)行前后自動處理事務的開啟、提交和回滾,確保數據的一致性和完整性。
- 3. 權限控制
- 實現方法的訪問控制,檢查用戶權限,確保只有授權用戶才能執(zhí)行特定操作。
- 4. 性能監(jiān)控
- 自動收集方法的執(zhí)行時間、調用次數等性能指標,幫助開發(fā)者進行性能分析和優(yōu)化。
- 5. 緩存管理
- 實現方法結果的緩存,減少重復計算,提高系統(tǒng)性能。
- 6. 異常處理
- 統(tǒng)一處理方法執(zhí)行中的異常,簡化異常捕獲和處理邏輯。
- 7. 輸入驗證
- 在方法執(zhí)行前對輸入參數進行驗證,保證數據的有效性和完整性。
- 8. 動態(tài)代理
- 創(chuàng)建代理對象,在不修改原有類的情況下添加額外的行為。
應用場景
- Web開發(fā):用于請求處理、事務管理、安全控制等。
- 企業(yè)應用:在服務層實現統(tǒng)一的日志和異常處理。
- 微服務:在服務之間進行統(tǒng)一的監(jiān)控和熔斷處理。
日志記錄
1.準備工作
首先需要導入AOP依賴
pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>com.alibaba.fastjson2</groupId> <artifactId>fastjson2</artifactId> <version>2.0.50</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency>
然后需要創(chuàng)建日志記錄數據庫和實體類還有在Mapper層實現插入數據方法
- 實體類
//操作日志實體類 @Data @NoArgsConstructor @AllArgsConstructor public class OperateLog { private Integer id; //主鍵ID private Integer operateUser; //操作人ID private LocalDateTime operateTime; //操作時間 private String className; //操作類名 private String methodName; //操作方法名 private String methodParams; //操作方法參數 private String returnValue; //操作方法返回值 private Long costTime; //操作耗時 }
- Mapper接口
@Mapper public interface OperateLogMapper { //插入日志數據 @Insert("insert into operate_log (operate_user, operate_time, class_name, method_name, method_params, return_value, cost_time) " + "values (#{operateUser}, #{operateTime}, #{className}, #{methodName}, #{methodParams}, #{returnValue}, #{costTime});") public void insert(OperateLog log); }
2.創(chuàng)建自定義注解和切面類
因為我們要記錄的操作日志是對于數據的增,刪,改所以采用@annotation來指定要為連接點的方法。
而且在涉及到消耗時間的字段,所以需要采用@Around通知類型
- 自定義注解類
@Retention(RetentionPolicy.RUNTIME)//運行時有效 @Target(ElementType.METHOD)//作用在方法上 public @interface Log { }
3.實現日志記錄(Around)
首先需要需要記錄的操作接口方法前加上@Log注解
接下來在切面類中實現操作日志記錄:
@Aspect @Component public class LogAsper { @Autowired private OperateLogMapper operateLogMapper; @Autowired private HttpServletRequest request; @Around("@annotation(com.ly.springbootdemotlias.anno.Log)")//匹配加了Log注解的方法 public Object recordLog(ProceedingJoinPoint joinPoint) throws Throwable { OperateLog operateLog = new OperateLog(); //獲取操作人id,通過獲取并解析jwt令牌 String jwt = request.getHeader("token"); Claims claims = JwtUtils.parseJwt((jwt)); Integer operateUser =(Integer) claims.get("id"); operateLog.setOperateUser(operateUser); //獲取操作時間 operateLog.setOperateTime(LocalDateTime.now()); //獲取操作類名 operateLog.setClassName(joinPoint.getTarget().getClass().getName()); //獲取操作方法名 operateLog.setMethodName(joinPoint.getSignature().getName()); //獲取操作方法參數 operateLog.setMethodParams(Arrays.toString(joinPoint.getArgs())); //獲取開始時間 long begin = System.currentTimeMillis(); //獲取操作方法返回值 Object result = joinPoint.proceed(); String returnValue = JSONObject.toJSONString(result); operateLog.setReturnValue(returnValue.toString()); //獲取操作結束時間 long end = System.currentTimeMillis(); //獲取操作耗時 operateLog.setCostTime((end-begin)); operateLogMapper.insert(operateLog); return result; } }
管理事務
1. 添加依賴
在 pom.xml 中添加 MyBatis-Plus 和 AOP 的依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.0</version> <!-- 根據需要選擇版本 --> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>
2. 配置 MyBatis-Plus
在 application.yml 中配置 MyBatis-Plus 和數據源:
spring: datasource: url: jdbc:mysql://localhost:3306/your_database username: your_username password: your_password driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: mapper-locations: classpath*:/mappers/**/*.xml
3. 創(chuàng)建事務切面
創(chuàng)建一個事務切面類:
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @Aspect @Component public class TransactionAspect { @Transactional @Around("execution(* com.yourpackage.service..*(..))") // 指定需要事務的業(yè)務邏輯包 public Object manageTransaction(ProceedingJoinPoint joinPoint) throws Throwable { Object result; try { result = joinPoint.proceed(); // 執(zhí)行目標方法 return result; } catch (Exception e) { throw e; // 拋出異常以觸發(fā)回滾 } } }
4. 示例業(yè)務邏輯類
創(chuàng)建一個服務類,使用 MyBatis-Plus 進行數據庫操作:
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.stereotype.Service; @Service public class UserService extends ServiceImpl<UserMapper, User> { public void createUser(User user) { this.save(user); // 調用 MyBatis-Plus 的 save 方法 } }
5. 用戶實體和 Mapper
- 定義用戶實體:
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; @TableName("users") // 數據庫表名 public class User { @TableId private Long id; private String name; // getters and setters }
- Mapper 接口:
import com.baomidou.mybatisplus.core.mapper.BaseMapper; public interface UserMapper extends BaseMapper<User> { // 可以自定義額外的方法 }
6. 創(chuàng)建控制器
在控制器中調用 UserService 的 createUser 方法:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/users") public class UserController { @Autowired private UserService userService; @PostMapping public String createUser(@RequestBody User user) { userService.createUser(user); // 調用 UserService 的 createUser 方法 return "User created successfully"; // 返回成功消息 } }
7. 啟動類
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
心得
- 通過以上步驟,你可以在 Spring Boot 項目中整合 AOP 和 MyBatis-Plus,實現簡單而有效的事務管理。
- 這樣,無論是在正常情況下提交事務,還是在出現異常時回滾事務,都能得到很好的支持。
為什么做事務,做事務有啥用
- 事務確保一組操作要么全部成功,要么全部失敗,從而保持數據的一致性和完整性。
- 它們在處理銀行轉賬、訂單處理等場景時尤其重要,可以防止數據不一致、部分更新或系統(tǒng)故障導致的數據丟失。
- 通過事務,確保即使在異常情況下,數據狀態(tài)依然可靠。
那為什么要結合aop去做事務
- 結合 AOP(面向切面編程)可以將事務管理的邏輯與業(yè)務邏輯分離,提高代碼的可維護性和可讀性。
- 通過 AOP,可以在不修改業(yè)務代碼的情況下,集中管理事務,簡化代碼結構,并實現事務的自動化管理,減少重復代碼,提高開發(fā)效率。
權限控制
1. 添加依賴
在 pom.xml 中添加 AOP 相關依賴:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
2. 創(chuàng)建權限注解
定義一個自定義注解 @RequiresPermission:
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RequiresPermission { String value(); }
3. 創(chuàng)建切面類
創(chuàng)建切面類,使用 @Aspect 注解來定義切面,處理權限控制邏輯:
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component @Aspect public class PermissionAspect { @Autowired private UserService userService; // 獲取用戶服務 @Around("@annotation(requiresPermission)") // 攔截使用 @RequiresPermission 注解的方法 public Object checkPermission(ProceedingJoinPoint joinPoint, RequiresPermission requiresPermission) throws Throwable { String requiredPermission = requiresPermission.value(); // 獲取所需權限 // 權限檢查邏輯 if (!hasPermission(requiredPermission)) { throw new SecurityException("Permission denied: " + requiredPermission); } return joinPoint.proceed(); // 執(zhí)行目標方法 } private boolean hasPermission(String permission) { User currentUser = userService.getCurrentUser(); // 獲取當前用戶 if (currentUser == null) { return false; // 用戶未登錄 } return currentUser.getPermissions().contains(permission); // 檢查權限 } }
在使用 AOP 時,@RequiresPermission 注解是一個標記,它本身并不直接傳遞參數。Spring AOP 在攔截帶有該注解的方法時,會自動創(chuàng)建該注解的實例,并將其作為參數傳遞給
checkPermission 方法。這是通過 AOP 框架的代理機制實現的,而不是通過顯式調用。具體來說,當被注解的方法被調用時,AOP 會攔截這個調用并注入相應的注解信息。
permission 參數來源于 @RequiresPermission 注解的 value() 方法。在 checkPermission 方法中,當該方法攔截到一個使用 @RequiresPermission 注解的方法時,Spring AOP 會將該注解的實例作為第二個參數傳遞給 checkPermission 方法。因此,你可以通過調用 requiresPermission.value() 來獲取注解中定義的權限字符串。
4. 使用權限注解
在需要進行權限控制的方法上使用自定義注解
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class MyController { @GetMapping("/admin") @RequiresPermission("ADMIN_ACCESS") public String adminAccess() { return "Welcome, admin!"; } }
5. 配置 Spring Boot
確保 Spring Boot 應用程序能夠掃描到切面和自定義注解。通常情況下,只需在主類中添加 @EnableAspectJAutoProxy 注解:
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.EnableAspectJAutoProxy; @SpringBootApplication @EnableAspectJAutoProxy public class MyApplication { public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } }
@EnableAspectJAutoProxy 注解的作用是啟用 Spring 的 AOP(面向切面編程)支持,允許使用 AspectJ 的代理機制來實現切面。具體來說,它有以下幾個功能:
1.啟用代理
它告訴 Spring 創(chuàng)建代理對象,這些代理對象可以攔截方法調用并執(zhí)行切面邏輯。
2.支持注解
當你在代碼中使用注解(如 @Around、@Before 等)時,@EnableAspectJAutoProxy 會使這些注解生效,從而實現方法攔截和切面邏輯的執(zhí)行。
3.簡化配置
通過添加這個注解,開發(fā)者不需要額外的 XML 配置,只需通過注解來定義切面和增強,減少了配置的復雜性。
在主類中添加這個注解后,Spring Boot 應用會自動掃描到切面類并將其注冊到應用上下文中,從而實現所需的 AOP 功能。
AOP的五種通知類型包括
- 前置通知(@Before)
- 后置通知(@After)
- 返回通知(@AfterReturning)
- 異常通知(@AfterThrowing)
- 環(huán)繞通知(@Around)
每種通知類型都有其特定的用途和場景,可以根據實際需求選擇使用。
總結
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
springboot?+rabbitmq+redis實現秒殺示例
本文主要介紹了springboot?+rabbitmq+redis實現秒殺示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-07-07SpringCloud如何使用Eureka實現服務之間的傳遞數據
這篇文章主要介紹了SpringCloud使用Eureka實現服務之間的傳遞數據操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06