依賴引入
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.21</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.21</version>
</dependency>
切面類實現
@Aspect
@Component
public class LoginCheckAspect {
// 環繞通知:攔截所有 @RequestMapping 註解的方法
@Around("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
public Object checkUser(ProceedingJoinPoint pjp) throws Throwable {
// 1. 獲取方法參數(如 HttpServletRequest)
Object[] args = pjp.getArgs();
HttpServletRequest request = Arrays.stream(args)
.filter(HttpServletRequest.class::isInstance)
.map(HttpServletRequest.class::cast)
.findFirst().orElse(null);
// 2. 驗證登入狀態(示例:Session 中是否有 user 屬性)
if (request != null && request.getSession().getAttribute("user") == null) {
throw new RuntimeException("未登入,請先登入!");
}
// 3. 前置日誌
System.out.println("【AOP】進入方法:" + pjp.getSignature().getName());
// 4. 執行目標方法
Object result = pjp.proceed();
// 5. 後置日誌
System.out.println("【AOP】方法執行完成");
return result;
}
// 前置通知:Controller 層所有方法
@Before("execution(* com.homework.controller..*.*(..))")
public void logBefore() {
System.out.println("【AOP】Controller 方法即將執行");
}
}
關鍵點解析
@Around 需手動呼叫 pjp.proceed() 繼續執行,可完整控制前後邏輯、異常捕獲、返回值修改。
@Before 僅執行前置邏輯,無法阻斷流程。
- 切點表達式:
@annotation(...) 針對註解;execution(...) 針對包/類/方法簽名。
- 註入
HttpServletRequest 時需從 JoinPoint.getArgs() 手動篩選,因 AOP 代理非 Controller 直接註入。
進階:自定義權限註解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequirePermission {
String value(); // 權限標識,如 "user:delete"
}
@Around("@annotation(requirePermission)")
public Object checkPermission(ProceedingJoinPoint pjp, RequirePermission requirePermission) {
String perm = requirePermission.value();
// 從 SecurityContext / JWT / Session 獲取當前用戶權限集合
if (!currentUserHasPermission(perm)) throw new AccessDeniedException("權限不足:" + perm);
return pjp.proceed();
}