Spring AOP總結

bladestone發表於2019-03-25

Terminology

Joint Point: public method in Spring Managed Bean
Point cut: The actual joint point that that we have delcared
Advice: The fucntionality we want to apply
Aspect: combine point cut and advice
Weaving: The process of applying an aspect to our system

Maven類庫

引用如下類庫說明:

    <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>

啟用Aspect AOP

在Spring Boot應用中的Configuration類上使用@EnableAspectJAutoProxy,在Spring應用中啟動對於Aspect功能支援。
例如:

@Configuration
@Slf4j
@EnableAspectJAutoProxy
public class AppConfig {
    @Bean(initMethod = "init", destroyMethod = "clearup")
    public Game game() {
        return new Game();
    }

    @Bean
    public Rain rain(Game game) {
        return new Rain(game);
    }
}

常用Annotation

@Aspect – 作用是把當前類標識為一個切面供容器讀取
@Pointcut – (切入點):就是帶有通知的連線點,在程式中主要體現為書寫切入點表示式
@Before – 標識一個前置增強方法,相當於BeforeAdvice的功能
@AfterReturning – 後置增強,相當於AfterReturningAdvice,方法退出時執行
@AfterThrowing – 異常丟擲增強,相當於ThrowsAdvice
@After – final增強,不管是丟擲異常或者正常退出都會執行
@Around – 環繞增強,相當於MethodInterceptor

應用場景

通過預編譯方式和執行期動態代理實現程式功能的一種開發技術。在日常開發當中經常用來記錄日誌,方法跟蹤、事務,許可權等

使用AoP

示例如下:

@Aspect
@Component
@Slf4j
public class LoggingAspect {
    /*
    @Before("execution(void org.cjf.beans..*.set*(*))")
    public void callSetter(JoinPoint joinPoint) {
        log.info("setter is invoked....");
        log.info("method:{}, params:{}, target:{}", joinPoint.getSignature().getName(),
                joinPoint.getArgs(), joinPoint.getTarget());
    }
    */

    @Around("execution(String playGame())")
    public Object checkForRain(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
       boolean rain = Math.random() < 0.5;

       Object result = null;
       if (rain) {
           log.info(proceedingJoinPoint.getTarget() + " rain out");
       }
       else {
           result = proceedingJoinPoint.proceed();
           log.info("result:{}", result.toString());
       }

       return result;
    }
}

測試用例2:

@Aspect
@Component
@Slf4j
public class LoggerAspect {
    /**
     * controller目錄下,所有類的所有方法
     */
    @Pointcut("execution(public * org.cjf.beans.controller..*.*(..))")
    public void getPointCut() {
    }

    //前置通知
    @Before("getPointCut()")
    public void beforeMethod(JoinPoint joinPoint){
        log.info("呼叫了前置通知");

    }

    //@After: 後置通知
    @After("getPointCut()")
    public void afterMethod(JoinPoint joinPoint){
        log.info("呼叫了後置通知");
    }

    //@AfterRunning: 返回通知 rsult為返回內容
    @AfterReturning(value="getPointCut()",returning="result")
    public void afterReturningMethod(JoinPoint joinPoint,Object result){
        log.info("呼叫了返回通知");
    }

    //@AfterThrowing: 異常通知
    @AfterThrowing(value="getPointCut()",throwing="e")
    public void afterReturningMethod(JoinPoint joinPoint, Exception e){
        log.info("呼叫了異常通知");
    }

    //@Around:環繞通知
    @Around("getPointCut()")
    public Object Around(ProceedingJoinPoint pjp) throws Throwable {
        log.info("around執行方法之前");
        Object object = pjp.proceed();
        log.info("around執行方法之後--返回值:" +object);
        return object;
    }
}

在這裡展示了@Around/@Before兩個註解的使用方法,其中使用execution來定義aop的切入點,其可以支援方法,一類方法和所有方法等各種粒度的捕獲和攔截。

詳細使用可以參考Spring的官方文件: https://docs.spring.io/spring/docs/5.1.5.RELEASE/spring-framework-reference/core.html#aop-extensibility

總結

AOP可以提供一種無侵入方式的方法增強和攔截,幫助開發者實現各類靈活性與功能增強的使用。