springAOP即面向切面編程,可以在方法執行過程中動態的織入增強邏輯,其使用步驟為:
1. 導入aop模塊的jar包,或在maven中添加依賴:spring-aspects
2. 定義目標類和目標方法,即需要增強的類和方法
3. 定義切面類和通知方法
4. 指定通知方法何時何地織入,即在切面類中添加切點和切面注解
5. 將目標類和切面類注冊到同一個springIOC容器中
6. 告訴容器哪個是目標類、哪個是切面類,即在切面類上添加@Aspect注解
7. 如果容器中存在多個切面類且需要排序,可以讓切面類實現Ordered接口
8. 開啟基於注解的切面功能,即在切面類上添加@EnableAspectJAutoProxy注解
目標類
/** * springAOP中的目標類 */ public class MathTool { /** * 執行除法邏輯 * @param i 被除數 * @param j 除數 * @return 商 */ public double divide(int i, int j) { System.out.println("--------目標方法開始執行--------"); double result = i / j; return result; } }
切面類
/** * 打印日志的切面類 */ @Aspect @EnableAspectJAutoProxy public class LogAspect implements Ordered { //定義切點,即需要增強的方法 @Pointcut(value = "execution(public double cn.monolog.entity.MathTool.*(..))") public void getPointcut() {} /** * 在目標方法之前執行的通知方法 * @param joinPoint 切點 */ @Before(value = "getPointcut()") public void startLog(JoinPoint joinPoint) { Object[] args = joinPoint.getArgs(); System.out.println("方法" + joinPoint.getSignature() + "即將執行,參數列表:" + Arrays.asList(args)); } /** * 在目標方法之后執行的通知方法 * @param joinPoint 切點 */ @After(value = "getPointcut()") public void endLog(JoinPoint joinPoint) { System.out.println("方法" + joinPoint.getSignature() + "執行完畢"); } /** * 目標方法正常返回時的通知方法 * @param joinPoint 切點 * @param result 目標方法的返回值,通過@AfterReturning定義 * 注:當通知方法存在多個參數時,JoinPoint必須放在第一位 */ @AfterReturning(value = "getPointcut()", returning = "result") public void returningLog(JoinPoint joinPoint, Object result) { System.out.println("方法" + joinPoint.getSignature() + "返回值為:" + result); } /** * 目標方法發生異常時的通知方法 * @param joinPoint 切點 * @param exception 目標方法拋出的異常,通過@AfterThrowing定義 * 注:當通知方法存在多個參數時,JoinPoint必須放在第一位 */ @AfterThrowing(value = "getPointcut()", throwing = "exception") public void exceptionLog(JoinPoint joinPoint, Exception exception) { System.out.println("方法" + joinPoint.getSignature() + "拋出異常,異常信息為:" + exception); } //Ordered接口提供的排序方法,返回值為執行順序,數字越小越靠前 @Override public int getOrder() { return 1; } }
容器配置類
/** * 容器配置類 * 用於測試springAOP * 需要注冊目標類和切面類 */ @Configuration @Import({AopDefinitionRegistrar.class}) public class AopBeanConfig { } /** * 用於向容器中導入需要的組件 */ public class AopDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //注冊目標類 BeanDefinition mathTool = new RootBeanDefinition(MathTool.class); registry.registerBeanDefinition("mathTool", mathTool); //注冊切面類 BeanDefinition logAspect = new RootBeanDefinition(LogAspect.class); registry.registerBeanDefinition("logAspect", logAspect); } }
springAOP的實現原理
1. @EnableAspectJAutoProxy開啟注解功能,並在容器中注冊一個組件——AnnotationAwareAspectJProxyCreator,這個組件實現了SmartInstantiationAwareBeanPostProcessor,是一個后置處理器;
2. 在創建springIOC容器時,有一個步驟是refresh即刷新容器,在這個方法中,有一步是registerBeanPostProcessors,這一步會初始化所有的后置處理器,就是在這時生成了AnnotationAwareAspectJProxyCreator組件;
3. refresh后面還有一步,finishBeanFactoryInitilization,即初始化剩下的單實例bean,這時會生成目標類組件和切面類組件;
4. AnnotationAwareAspectJProxyCreator會對目標類組件和切面類組件進行攔截,即在這些組件創建完成並初始化之后,調用postProcessAfterInitialization方法,判斷目標類組件是否需要增強,如果需要,會將切面類的通知方法包裝成增強器(Advisor),然后用cglib動態代理(如果目標類實現了接口,也可以使用jdk動態代理)給目標類對象創建一個代理對象,這個代理對象中就有上述增強器;
5. 經過2-4步,容器就創建完畢,接下來代理對象執行目標方法,首先獲取目標方法的攔截器鏈(即MethodInterceptor,由增強器包裝而來),利用攔截器的鏈式機制依次進入每一個攔截器進行增強;
6. 攔截器鏈的執行效果
目標方法成功:前置通知 → 目標方法 → 后置通知 → 返回通知;
目標方法拋出異常:前置通知 → 目標方法 → 后置通知 → 異常通知。