天天看點

Spring進階之AOP

作者:愛吃中餐的鄭鄭鄭鄭

AOP:面向切面程式設計,在不修改目标類代碼的前提下,可以通過AOP技術去增強目标類的功能。通過預編譯方式和運作期動态代理實作程式功能的統一維護的一種技術。

利用AOP可以對業務代碼中【業務邏輯】和【系統邏輯】進行隔離,進而使得代碼耦合度降低,提高程式的可用性,同時提高開發效率。

AOP采用橫向抽取的機制,補充了傳統縱向內建體系OOP(面向對象程式設計)無法解決的重複性代碼優化(系統檢測、事務管理、安全檢查、緩存),将業務邏輯和系統處理的代碼(關閉連接配接、事務操作、日志記錄)解耦。

AOP相關術語介紹:

Joinpoint(連接配接點):指被攔截到的點,這些點指的是方法,spring隻支援方法類型的連接配接點。

Pointcut(切入點):指我們要對哪些Joinpoint進行攔截定義。

Advice(通知\增強):指攔截到Joinpoint之後要做的事情就是通知,通知分為前置通知、後置通知、異常通知、最終通知、環繞通知(切面要完成的功能)。

Introduction(引介):引介是一種特殊的通知在不修改類代碼的前提下,Introduction可以在運作期為類動态的添加一些方法或Field。

Target(目标對象):代理的目标對象。

Weading(織入):是指把增強應用到目标對象來建立新的代理對象的過程。

Proxy(代理):一個類被AOP組織增強後,就産生一個代理結果類。

Aspect(切面):切入點和通知的結合,需要自己編寫和配置。

Advisor(通知器、顧問):和Aspect很像。

基于注解實作:

  • 定義注解
package com.zww.demo.aop.annotation;
import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ILog {
//    String flag default ""
}
           
  • 定義方法
package com.zww.demo.aop.controller;
import com.zww.demo.aop.annotation.ILog;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AopController {

    @ILog
    @GetMapping("/test")
    public void test(){
//        int i = 1/0;
        System.out.println("hello!");
    }
}
           
  • 定義切面
package com.zww.demo.aop.advice;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;

@Component
/** 切面 */
@Aspect
public class LogAdvice {

    /** 切點
     * @annotation 注解,可配置系統注解或者自定義注解,隻要該方法添加該注解即接入切面
     * 例:@annotation(com.zww.demo.aop.annotation.ILog)
     * execution 表達式,通過全限定類名,方法比對
     * 例:execution(public * com.zww.demo.aop.controller..*(..))
     */
//    @Pointcut("execution(public * com.zww.demo.aop.controller..*(..))")
    @Pointcut("@annotation(com.zww.demo.aop.annotation.ILog)")
    public void logPointcut(){};

    @Before("logPointcut()")
    public void before(JoinPoint point){
        System.out.println("前置通知");
        // TODO
        // 擷取簽名
        Signature signature = point.getSignature();
        System.out.println("執行方法:"+signature.getName()+", 包:"+signature.getDeclaringTypeName());

        // 可以記錄一些請求資訊,URL和IP
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        String url = request.getRequestURL().toString();
        String remoteAddr = request.getRemoteAddr();
        System.out.println("請求位址:"+url+",請求ip:"+remoteAddr);
    }

    @After("logPointcut()")
    public void after(JoinPoint point){
        // TODO
        System.out.println("後置通知");
    }

    @Around("logPointcut()")
    public Object around(ProceedingJoinPoint joinPoint){
        // TODO
        // 擷取請求參數
        Object[] args = joinPoint.getArgs();
        Object result;
        try {
            // @Before
            System.out.println("環繞前置通知");
            result = joinPoint.proceed(args);
            // @AfterReturning
            System.out.println("環繞傳回通知");
        } catch (Throwable e) {
            // @AfterThrowing
            System.out.println("環繞異常通知");
            throw new RuntimeException(e);
        } finally {
            // @after
            System.out.println("環繞後置通知");
        }
        return result;
    }

    @AfterReturning("logPointcut()")
    public void afterReturning(JoinPoint point){
        System.out.println("傳回通知");
    }

    @AfterThrowing("logPointcut()")
    public void afterThrowing(JoinPoint point){
        System.out.println("異常通知");
    }
}
           
  • 驗證結果
Spring進階之AOP