1. 自定義注釋是基于SpringAOP實現的
Spring AOP(Aspect-Oriented Programming,面向切面編程)是Spring框架中的一個強大功能模塊,它實現了AOP編程模型,允許開發者將橫切關注點(如日志記錄、事務管理、安全性檢查、性能監控等)從業務邏輯中分離出來,以提高代碼的模塊化程度、可維護性和可重用性。
核心概念
- 切面(Aspect):切面是跨越多個對象的關注點模塊化方式的實現。它封裝了橫切關注點,比如事務管理就是企業級應用中的一個關注點,它可能會影響到多個對象的操作。
- 連接點(Joinpoint):在程序執行過程中的某個特定點,如方法調用或異常拋出等,其中可以插入切面代碼。Spring AOP只支持方法執行作為連接點。
切入點(Pointcut):切入點定義了切面在何處應用,即匹配連接點的一組規則。通過表達式來指定哪些方法或類應該被切面影響。 - 通知(Advice):在切面識別到特定的連接點時執行的動作。有五種類型的通知:
- 前置通知(Before):在目標方法被調用之前執行。
- 后置通知(After):在目標方法執行完畢后(無論是否發生異常)執行。
- 返回通知(AfterReturning):在目標方法成功執行后執行。
- 異常通知(AfterThrowing):在目標方法拋出異常后執行。
- 環繞通知(Around):圍繞著目標方法執行,在方法調用前后都可以進行自定義操作,還可以決定是否繼續執行目標方法。
- 織入(Weaving):將切面代碼插入到應用程序代碼中的過程。Spring AOP支持兩種織入方式:編譯期織入和運行時織入,Spring采用的是運行時織入,即在應用運行時通過動態代理來實現。
實現方式
Spring AOP提供了兩種代理方式來實現切面邏輯的織入:
- JDK動態代理:當目標對象實現了至少一個接口時,Spring會使用JDK動態代理技術創建代理對象。這種方式的代理對象需要與目標對象實現相同的接口。
- CGLIB代理:如果目標對象沒有實現接口,Spring會使用CGLIB庫來創建目標對象的子類代理。這種方式對于沒有接口的類同樣適用,但要求目標類不能是final的,且必須有默認構造函數。
使用Spring AOP
在Spring中使用AOP,通常涉及定義切面類(使用@Aspect注解標記)、定義切入點(使用@Pointcut注解)、以及在切點上應用通知(使用如@Before、@After等注解)。通過這些配置,Spring框架會在運行時自動創建代理對象,將切面邏輯編織進目標對象的方法調用流程中,從而實現非侵入式的橫切關注點管理。
2. 使用自定義注釋
步驟1: 定義自定義注解
首先,我們定義一個自定義注解@LogExecutionTime,用于標記需要記錄執行時間的方法。
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 LogExecutionTime {
}
步驟2: 創建切面類
接著,我們創建一個切面類LoggingAspect,使用@Aspect注解標記,并在其中定義切點和通知邏輯。
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;@Aspect
@Component
public class LoggingAspect {private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);@Around("@annotation(LogExecutionTime)")public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {long start = System.currentTimeMillis();Object proceed = joinPoint.proceed(); // 執行原方法long elapsedTime = System.currentTimeMillis() - start;logger.info("Method {} executed in {} ms", joinPoint.getSignature().getName(), elapsedTime);return proceed;}
}
步驟3: 應用自定義注解
現在,我們可以在任何想要記錄執行時間的方法上使用@LogExecutionTime注解。
@Service
public class MyService {@LogExecutionTimepublic String performTask() {// 模擬耗時操作try {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();throw new RuntimeException(e);}return "Task completed!";}
}
步驟4: 配置Spring啟用AOP
確保Spring知道要使用AOP。
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {// 其他配置...
}