SpringBoot整合Aop全過程
AOP 可以做什么?
- 1. 日志記錄
- 自動(dòng)記錄方法的調(diào)用、參數(shù)、返回值和異常等信息,減少了在每個(gè)方法中添加日志代碼的需要。
- 2. 管理事務(wù)
- 在方法執(zhí)行前后自動(dòng)處理事務(wù)的開啟、提交和回滾,確保數(shù)據(jù)的一致性和完整性。
- 3. 權(quán)限控制
- 實(shí)現(xiàn)方法的訪問控制,檢查用戶權(quán)限,確保只有授權(quán)用戶才能執(zhí)行特定操作。
- 4. 性能監(jiān)控
- 自動(dòng)收集方法的執(zhí)行時(shí)間、調(diào)用次數(shù)等性能指標(biāo),幫助開發(fā)者進(jìn)行性能分析和優(yōu)化。
- 5. 緩存管理
- 實(shí)現(xiàn)方法結(jié)果的緩存,減少重復(fù)計(jì)算,提高系統(tǒng)性能。
- 6. 異常處理
- 統(tǒng)一處理方法執(zhí)行中的異常,簡化異常捕獲和處理邏輯。
- 7. 輸入驗(yàn)證
- 在方法執(zhí)行前對輸入?yún)?shù)進(jìn)行驗(yàn)證,保證數(shù)據(jù)的有效性和完整性。
- 8. 動(dòng)態(tài)代理
- 創(chuàng)建代理對象,在不修改原有類的情況下添加額外的行為。
應(yīng)用場景
- Web開發(fā):用于請求處理、事務(wù)管理、安全控制等。
- 企業(yè)應(yīng)用:在服務(wù)層實(shí)現(xiàn)統(tǒng)一的日志和異常處理。
- 微服務(wù):在服務(wù)之間進(jìn)行統(tǒng)一的監(jiān)控和熔斷處理。
日志記錄
1.準(zhǔn)備工作
首先需要導(dǎo)入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>然后需要?jiǎng)?chuàng)建日志記錄數(shù)據(jù)庫和實(shí)體類還有在Mapper層實(shí)現(xiàn)插入數(shù)據(jù)方法
- 實(shí)體類
//操作日志實(shí)體類
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OperateLog {
private Integer id; //主鍵ID
private Integer operateUser; //操作人ID
private LocalDateTime operateTime; //操作時(shí)間
private String className; //操作類名
private String methodName; //操作方法名
private String methodParams; //操作方法參數(shù)
private String returnValue; //操作方法返回值
private Long costTime; //操作耗時(shí)
}- Mapper接口
@Mapper
public interface OperateLogMapper {
//插入日志數(shù)據(jù)
@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)建自定義注解和切面類
因?yàn)槲覀円涗浀牟僮魅罩臼菍τ跀?shù)據(jù)的增,刪,改所以采用@annotation來指定要為連接點(diǎn)的方法。
而且在涉及到消耗時(shí)間的字段,所以需要采用@Around通知類型
- 自定義注解類
@Retention(RetentionPolicy.RUNTIME)//運(yùn)行時(shí)有效
@Target(ElementType.METHOD)//作用在方法上
public @interface Log {
}3.實(shí)現(xiàn)日志記錄(Around)
首先需要需要記錄的操作接口方法前加上@Log注解
接下來在切面類中實(shí)現(xiàn)操作日志記錄:
@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);
//獲取操作時(shí)間
operateLog.setOperateTime(LocalDateTime.now());
//獲取操作類名
operateLog.setClassName(joinPoint.getTarget().getClass().getName());
//獲取操作方法名
operateLog.setMethodName(joinPoint.getSignature().getName());
//獲取操作方法參數(shù)
operateLog.setMethodParams(Arrays.toString(joinPoint.getArgs()));
//獲取開始時(shí)間
long begin = System.currentTimeMillis();
//獲取操作方法返回值
Object result = joinPoint.proceed();
String returnValue = JSONObject.toJSONString(result);
operateLog.setReturnValue(returnValue.toString());
//獲取操作結(jié)束時(shí)間
long end = System.currentTimeMillis();
//獲取操作耗時(shí)
operateLog.setCostTime((end-begin));
operateLogMapper.insert(operateLog);
return result;
}
}管理事務(wù)
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> <!-- 根據(jù)需要選擇版本 -->
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>2. 配置 MyBatis-Plus
在 application.yml 中配置 MyBatis-Plus 和數(shù)據(jù)源:
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/**/*.xml3. 創(chuàng)建事務(wù)切面
創(chuàng)建一個(gè)事務(wù)切面類:
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..*(..))") // 指定需要事務(wù)的業(yè)務(wù)邏輯包
public Object manageTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
Object result;
try {
result = joinPoint.proceed(); // 執(zhí)行目標(biāo)方法
return result;
} catch (Exception e) {
throw e; // 拋出異常以觸發(fā)回滾
}
}
}4. 示例業(yè)務(wù)邏輯類
創(chuàng)建一個(gè)服務(wù)類,使用 MyBatis-Plus 進(jìn)行數(shù)據(jù)庫操作:
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); // 調(diào)用 MyBatis-Plus 的 save 方法
}
}5. 用戶實(shí)體和 Mapper
- 定義用戶實(shí)體:
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@TableName("users") // 數(shù)據(jù)庫表名
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)建控制器
在控制器中調(diào)用 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); // 調(diào)用 UserService 的 createUser 方法
return "User created successfully"; // 返回成功消息
}
}7. 啟動(dòng)類
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 項(xiàng)目中整合 AOP 和 MyBatis-Plus,實(shí)現(xiàn)簡單而有效的事務(wù)管理。
- 這樣,無論是在正常情況下提交事務(wù),還是在出現(xiàn)異常時(shí)回滾事務(wù),都能得到很好的支持。
為什么做事務(wù),做事務(wù)有啥用
- 事務(wù)確保一組操作要么全部成功,要么全部失敗,從而保持?jǐn)?shù)據(jù)的一致性和完整性。
- 它們在處理銀行轉(zhuǎn)賬、訂單處理等場景時(shí)尤其重要,可以防止數(shù)據(jù)不一致、部分更新或系統(tǒng)故障導(dǎo)致的數(shù)據(jù)丟失。
- 通過事務(wù),確保即使在異常情況下,數(shù)據(jù)狀態(tài)依然可靠。
那為什么要結(jié)合aop去做事務(wù)
- 結(jié)合 AOP(面向切面編程)可以將事務(wù)管理的邏輯與業(yè)務(wù)邏輯分離,提高代碼的可維護(hù)性和可讀性。
- 通過 AOP,可以在不修改業(yè)務(wù)代碼的情況下,集中管理事務(wù),簡化代碼結(jié)構(gòu),并實(shí)現(xiàn)事務(wù)的自動(dòng)化管理,減少重復(fù)代碼,提高開發(fā)效率。
權(quán)限控制
1. 添加依賴
在 pom.xml 中添加 AOP 相關(guān)依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>2. 創(chuàng)建權(quán)限注解
定義一個(gè)自定義注解 @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 注解來定義切面,處理權(quán)限控制邏輯:
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; // 獲取用戶服務(wù)
@Around("@annotation(requiresPermission)") // 攔截使用 @RequiresPermission 注解的方法
public Object checkPermission(ProceedingJoinPoint joinPoint, RequiresPermission requiresPermission) throws Throwable {
String requiredPermission = requiresPermission.value(); // 獲取所需權(quán)限
// 權(quán)限檢查邏輯
if (!hasPermission(requiredPermission)) {
throw new SecurityException("Permission denied: " + requiredPermission);
}
return joinPoint.proceed(); // 執(zhí)行目標(biāo)方法
}
private boolean hasPermission(String permission) {
User currentUser = userService.getCurrentUser(); // 獲取當(dāng)前用戶
if (currentUser == null) {
return false; // 用戶未登錄
}
return currentUser.getPermissions().contains(permission); // 檢查權(quán)限
}
}在使用 AOP 時(shí),@RequiresPermission 注解是一個(gè)標(biāo)記,它本身并不直接傳遞參數(shù)。Spring AOP 在攔截帶有該注解的方法時(shí),會(huì)自動(dòng)創(chuàng)建該注解的實(shí)例,并將其作為參數(shù)傳遞給
checkPermission 方法。這是通過 AOP 框架的代理機(jī)制實(shí)現(xiàn)的,而不是通過顯式調(diào)用。具體來說,當(dāng)被注解的方法被調(diào)用時(shí),AOP 會(huì)攔截這個(gè)調(diào)用并注入相應(yīng)的注解信息。
permission 參數(shù)來源于 @RequiresPermission 注解的 value() 方法。在 checkPermission 方法中,當(dāng)該方法攔截到一個(gè)使用 @RequiresPermission 注解的方法時(shí),Spring AOP 會(huì)將該注解的實(shí)例作為第二個(gè)參數(shù)傳遞給 checkPermission 方法。因此,你可以通過調(diào)用 requiresPermission.value() 來獲取注解中定義的權(quán)限字符串。
4. 使用權(quán)限注解
在需要進(jìn)行權(quán)限控制的方法上使用自定義注解
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 應(yīng)用程序能夠掃描到切面和自定義注解。通常情況下,只需在主類中添加 @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 的代理機(jī)制來實(shí)現(xiàn)切面。具體來說,它有以下幾個(gè)功能:
1.啟用代理
它告訴 Spring 創(chuàng)建代理對象,這些代理對象可以攔截方法調(diào)用并執(zhí)行切面邏輯。
2.支持注解
當(dāng)你在代碼中使用注解(如 @Around、@Before 等)時(shí),@EnableAspectJAutoProxy 會(huì)使這些注解生效,從而實(shí)現(xiàn)方法攔截和切面邏輯的執(zhí)行。
3.簡化配置
通過添加這個(gè)注解,開發(fā)者不需要額外的 XML 配置,只需通過注解來定義切面和增強(qiáng),減少了配置的復(fù)雜性。
在主類中添加這個(gè)注解后,Spring Boot 應(yīng)用會(huì)自動(dòng)掃描到切面類并將其注冊到應(yīng)用上下文中,從而實(shí)現(xiàn)所需的 AOP 功能。
AOP的五種通知類型包括
- 前置通知(@Before)
- 后置通知(@After)
- 返回通知(@AfterReturning)
- 異常通知(@AfterThrowing)
- 環(huán)繞通知(@Around)
每種通知類型都有其特定的用途和場景,可以根據(jù)實(shí)際需求選擇使用。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
springboot?+rabbitmq+redis實(shí)現(xiàn)秒殺示例
本文主要介紹了springboot?+rabbitmq+redis實(shí)現(xiàn)秒殺示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07
Java實(shí)現(xiàn)異步延遲隊(duì)列的方法詳解
目前系統(tǒng)中有很多需要用到延時(shí)處理的功能,本文就為大家介紹了Java實(shí)現(xiàn)異步延遲隊(duì)列的方法,文中的示例代碼講解詳細(xì),需要的可以參考一下2023-03-03
Intellij IDEA 添加jar包的三種方式(小結(jié))
這篇文章主要介紹了Intellij IDEA 添加jar包的三種方式(小結(jié)),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-08-08
詳解spring batch的使用和定時(shí)器Quart的使用
spring Batch是一個(gè)基于Spring的企業(yè)級批處理框架,它通過配合定時(shí)器Quartz來輕易實(shí)現(xiàn)大批量的數(shù)據(jù)讀取或插入,并且全程自動(dòng)化,無需人員管理2017-08-08
java mybatis框架實(shí)現(xiàn)多表關(guān)系查詢功能
這篇文章主要介紹了java mybatis框架實(shí)現(xiàn)多表關(guān)系查詢,基于Maven框架的整體設(shè)計(jì) —— 一多一的關(guān)系,文中通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-10-10
java 獲取當(dāng)前函數(shù)名的實(shí)現(xiàn)代碼
以下是對使用java獲取當(dāng)前函數(shù)名的實(shí)現(xiàn)代碼進(jìn)行了介紹。需要的朋友可以過來參考下2013-08-08
SpringCloud如何使用Eureka實(shí)現(xiàn)服務(wù)之間的傳遞數(shù)據(jù)
這篇文章主要介紹了SpringCloud使用Eureka實(shí)現(xiàn)服務(wù)之間的傳遞數(shù)據(jù)操作,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06

