springAOP基於注解的使用方法和實現原理


 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. 攔截器鏈的執行效果

目標方法成功:前置通知 → 目標方法 → 后置通知 → 返回通知;

目標方法拋出異常:前置通知 → 目標方法 → 后置通知 → 異常通知。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM