Spring?AOP通知類型與實(shí)戰(zhàn)示例講解
更新時(shí)間:2024年11月18日 12:14:59 作者:lzz的編碼時(shí)刻
Spring?AOP提供了五種通知類型:@Before、@After、@AfterReturning、@AfterThrowing和@Around,每種通知類型都有其特定的使用場(chǎng)景和實(shí)現(xiàn)方式,通過合理使用這些通知類型,可以實(shí)現(xiàn)各種橫切關(guān)注點(diǎn)的模塊化和解耦,感興趣的朋友跟隨小編一起看看吧
1. @Before 前置通知
1.1 基本說明
- 在目標(biāo)方法執(zhí)行前執(zhí)行
- 不能阻止目標(biāo)方法執(zhí)行(除非拋出異常)
- 可以獲取目標(biāo)方法的參數(shù)信息
1.2 實(shí)現(xiàn)示例
@Aspect @Component public class SecurityAspect { @Before("@annotation(requiresAuth)") public void checkAuth(JoinPoint joinPoint, RequiresAuth requiresAuth) { // 獲取當(dāng)前用戶信息 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); String token = request.getHeader("Authorization"); // 驗(yàn)證token if (!tokenService.isValid(token)) { throw new UnauthorizedException("無效的認(rèn)證令牌"); } // 檢查權(quán)限 String requiredRole = requiresAuth.role(); if (!hasRole(token, requiredRole)) { throw new ForbiddenException("權(quán)限不足"); } } }
1.3 典型應(yīng)用場(chǎng)景
- 權(quán)限驗(yàn)證
- 參數(shù)驗(yàn)證
- 日志記錄
- 事務(wù)開始標(biāo)記
- 緩存預(yù)處理
1.4 獲取參數(shù)
1.4.1 基本參數(shù)獲取
@Before("execution(* com.example.service.*.*(..))") public void beforeAdvice(JoinPoint joinPoint) { // 獲取方法參數(shù) Object[] args = joinPoint.getArgs(); // 獲取方法簽名 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); String methodName = signature.getName(); // 獲取參數(shù)名稱 String[] parameterNames = signature.getParameterNames(); // 獲取參數(shù)類型 Class<?>[] parameterTypes = signature.getParameterTypes(); // 打印參數(shù)信息 for (int i = 0; i < args.length; i++) { logger.info("Parameter {} ({}) = {}", parameterNames[i], parameterTypes[i].getSimpleName(), args[i]); } }
1.4.2 獲取注解參數(shù)
@Before("@annotation(logParams)") public void beforeWithAnnotation(JoinPoint joinPoint, LogParams logParams) { // 直接獲取注解屬性 String description = logParams.description(); boolean logResult = logParams.logResult(); // 獲取方法參數(shù) Object[] args = joinPoint.getArgs(); // 根據(jù)注解配置記錄日志 if (logParams.includeParameters()) { Arrays.stream(args) .forEach(arg -> logger.info("Parameter value: {}", arg)); } }
2. @After 后置通知
2.1 基本說明
- 在目標(biāo)方法執(zhí)行后執(zhí)行(無論是否拋出異常)
- 不能訪問目標(biāo)方法的返回值
- 主要用于清理資源或類似的收尾工作
2.2 實(shí)現(xiàn)示例
@Aspect @Component public class ResourceCleanupAspect { @After("execution(* com.example.service.FileService.*(..))") public void cleanup(JoinPoint joinPoint) { try { // 清理臨時(shí)文件 String methodName = joinPoint.getSignature().getName(); logger.info("Cleaning up resources after method: {}", methodName); cleanupTempFiles(); // 釋放其他資源 releaseResources(); } catch (Exception e) { logger.error("Cleanup failed", e); } } private void cleanupTempFiles() { // 清理臨時(shí)文件的具體實(shí)現(xiàn) } private void releaseResources() { // 釋放資源的具體實(shí)現(xiàn) } }
2.3 典型應(yīng)用場(chǎng)景
- 資源清理
- 連接關(guān)閉
- 計(jì)數(shù)器更新
- 日志記錄
- 性能監(jiān)控結(jié)束標(biāo)記
2.4 參數(shù)獲取
@After("execution(* com.example.service.*.*(..)) && args(id,name,..)") public void afterAdvice(JoinPoint joinPoint, Long id, String name) { // 直接使用參數(shù) logger.info("Method executed with ID: {} and name: {}", id, name); // 獲取目標(biāo)類信息 Class<?> targetClass = joinPoint.getTarget().getClass(); // 獲取代理類信息 Class<?> proxyClass = joinPoint.getThis().getClass(); }
3. @AfterReturning 返回通知
3.1 基本說明
- 在目標(biāo)方法成功執(zhí)行后執(zhí)行
- 可以訪問目標(biāo)方法的返回值
- 可以修改返回值(通過包裝類)
3.2 實(shí)現(xiàn)示例
@Aspect @Component public class ResponseHandlerAspect { @AfterReturning( pointcut = "execution(* com.example.controller.*.*(..))", returning = "result" ) public void handleResponse(JoinPoint joinPoint, Object result) { if (result instanceof List) { // 對(duì)集合類型結(jié)果進(jìn)行脫敏處理 List<?> list = (List<?>) result; for (Object item : list) { if (item instanceof UserDTO) { UserDTO user = (UserDTO) item; user.setPhone(maskPhoneNumber(user.getPhone())); user.setEmail(maskEmail(user.getEmail())); } } } } private String maskPhoneNumber(String phone) { // 手機(jī)號(hào)碼脫敏邏輯 return phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2"); } private String maskEmail(String email) { // 郵箱脫敏邏輯 return email.replaceAll("(\\w{3})\\w+(@\\w+\\.\\w+)", "$1***$2"); } }
3.3 典型應(yīng)用場(chǎng)景
- 返回值修改(如數(shù)據(jù)脫敏)
- 統(tǒng)計(jì)方法成功率
- 緩存結(jié)果
- 結(jié)果格式化
- 數(shù)據(jù)集合包裝
4. @AfterThrowing 異常通知
4.1 基本說明
- 在目標(biāo)方法拋出異常時(shí)執(zhí)行
- 可以訪問拋出的異常信息
- 可以進(jìn)行異常轉(zhuǎn)換或處理
4.2 實(shí)現(xiàn)示例
@Aspect @Component public class ExceptionHandlerAspect { @AfterThrowing( pointcut = "execution(* com.example.service.*.*(..))", throwing = "ex" ) public void handleException(JoinPoint joinPoint, Exception ex) { String methodName = joinPoint.getSignature().getName(); String className = joinPoint.getTarget().getClass().getSimpleName(); // 記錄詳細(xì)錯(cuò)誤信息 logger.error("Exception in {}.{}: {}", className, methodName, ex.getMessage()); // 發(fā)送告警 if (ex instanceof DataAccessException) { alertService.sendDatabaseAlert(className, methodName, ex); } // 異常分類統(tǒng)計(jì) metricService.incrementExceptionCounter(className, methodName, ex.getClass().getSimpleName()); // 如果需要,可以轉(zhuǎn)換異常類型 if (ex instanceof SQLException) { throw new DatabaseException("數(shù)據(jù)庫操作失敗", ex); } } }
4.3 典型應(yīng)用場(chǎng)景
- 異常記錄
- 異常轉(zhuǎn)換
- 告警通知
- 失敗重試
- 錯(cuò)誤統(tǒng)計(jì)
5. @Around 環(huán)繞通知
5.1 基本說明
- 最強(qiáng)大的通知類型,可以完全控制目標(biāo)方法的執(zhí)行
- 可以在方法執(zhí)行前后添加自定義行為
- 可以修改方法的參數(shù)和返回值
- 可以決定是否執(zhí)行目標(biāo)方法
5.2 實(shí)現(xiàn)示例
@Aspect @Component public class CacheAspect { @Autowired private CacheManager cacheManager; @Around("@annotation(cacheable)") public Object handleCache(ProceedingJoinPoint joinPoint, Cacheable cacheable) throws Throwable { // 構(gòu)建緩存key String key = buildCacheKey(joinPoint, cacheable); // 嘗試從緩存獲取 Object cachedValue = cacheManager.get(key); if (cachedValue != null) { logger.debug("Cache hit for key: {}", key); return cachedValue; } // 執(zhí)行目標(biāo)方法 long startTime = System.currentTimeMillis(); Object result = null; try { result = joinPoint.proceed(); // 記錄執(zhí)行時(shí)間 long executionTime = System.currentTimeMillis() - startTime; logger.debug("Method execution time: {}ms", executionTime); // 如果執(zhí)行時(shí)間超過閾值,發(fā)送告警 if (executionTime > 1000) { alertService.sendPerformanceAlert(joinPoint, executionTime); } } catch (Exception e) { // 異常處理 logger.error("Method execution failed", e); throw e; } // 將結(jié)果放入緩存 if (result != null) { cacheManager.put(key, result, cacheable.ttl()); } return result; } private String buildCacheKey(ProceedingJoinPoint joinPoint, Cacheable cacheable) { // 緩存key構(gòu)建邏輯 StringBuilder key = new StringBuilder(); key.append(joinPoint.getSignature().getDeclaringTypeName()) .append(".") .append(joinPoint.getSignature().getName()); Object[] args = joinPoint.getArgs(); if (args != null && args.length > 0) { key.append(":"); for (Object arg : args) { key.append(arg != null ? arg.toString() : "null").append(","); } } return key.toString(); } }
5.3 典型應(yīng)用場(chǎng)景
- 方法緩存
- 性能監(jiān)控
- 事務(wù)處理
- 重試機(jī)制
- 并發(fā)控制
- 限流處理
@Aspect @Component public class RateLimiterAspect { private final RateLimiter rateLimiter = RateLimiter.create(100.0); // 每秒100個(gè)請(qǐng)求 @Around("@annotation(rateLimited)") public Object limitRate(ProceedingJoinPoint joinPoint, RateLimited rateLimited) throws Throwable { if (!rateLimiter.tryAcquire(100, TimeUnit.MILLISECONDS)) { throw new TooManyRequestsException("請(qǐng)求過于頻繁,請(qǐng)稍后重試"); } return joinPoint.proceed(); } }
6. 最佳實(shí)踐
- 選擇合適的通知類型
- 如果只需要前置處理,用@Before
- 如果需要訪問返回值,用@AfterReturning
- 如果需要處理異常,用@AfterThrowing
- 如果需要完全控制方法執(zhí)行,用@Around
性能考慮
- 避免在通知中執(zhí)行耗時(shí)操作
- 合理使用緩存
- 注意異常處理的性能影響
代碼組織
- 每個(gè)切面專注于單一職責(zé)
- 通知方法保持簡潔
- 復(fù)用共同的切入點(diǎn)表達(dá)式
異常處理
- 在通知中要做好異常處理
- 不要吞掉異常
- 適當(dāng)轉(zhuǎn)換異常類型
到此這篇關(guān)于Spring AOP通知類型詳解與實(shí)戰(zhàn)的文章就介紹到這了,更多相關(guān)Spring AOP通知類型內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot集成極光推送的實(shí)現(xiàn)代碼
工作中經(jīng)常會(huì)遇到服務(wù)器向App推送消息的需求,一般企業(yè)中選擇用極光推送的比較多,本文就介紹了SpringBoot集成極光推送的實(shí)現(xiàn)代碼,感興趣的可以了解一下2023-08-08Mybatisplus集成springboot完成分頁查詢功能(示例代碼)
今天小編給大家分享Mybatisplus集成springboot完成分頁查詢功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧2023-11-11Spring Security 安全框架應(yīng)用原理解析
這篇文章主要介紹了Spring Security 安全框架應(yīng)用,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-07-07Java使用easyExcel實(shí)現(xiàn)導(dǎo)入功能
這篇文章介紹了Java使用easyExcel實(shí)現(xiàn)導(dǎo)入功能的方法,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-10-10