1. 程式人生 > 程式設計 >面向切面(AOP)的理解和使用

面向切面(AOP)的理解和使用

是個什麼東西

面向物件,大家都聽過。那面向切面是個啥?面向切面是對面向物件的延伸。那就先從面向物件開始說起。

縱向抽取

在面向物件中, 對重複的邏輯程式碼會抽取出來,在需要用到的地方繼承即可。這就是縱向抽取

橫向抽象

但是,縱向抽取需求並不能滿足所有的抽取場景,比如在不同的方法有著一些重複的程式碼邏輯,通過縱向抽取是沒辦法抽取使用的,程式碼如下:

主業務和系統業務耦合

所以我們需要使用橫向切面的方式來對重複的邏輯程式碼進行抽取複用,這就是面向切面程式設計:

將分散在各個業務邏輯程式碼中相同的程式碼通過橫向切割的方式抽取到一個獨立的模組中

image

在日常的開發中,程式碼分為主業務和系統級業務,上圖中的左側的就是主業務邏輯,右側就是系統級業務。系統級業務都應該用切面的方式抽取到獨立的模組中,不和主業務程式碼耦合,真正實現寫程式碼時只需關注主業務的實現。

有哪些概念

相關概念

寫程式碼實踐

通過定義一個許可權檢查切面,來對所有的介面進行許可權校驗。

編寫兩個介面

一個新增介面,一個刪除介面,刪除介面需要userType為admin才有許可權刪除。


@RestController
public class WebController {

    @GetMapping("add")
    public String add(@RequestParam("userType") String userType){
        System.out.println("add:新增使用者...");
        return "ok";
    }

    @GetMapping(value = "del"
) public String del(@RequestParam("userType") String userType){ System.out.println("add:刪除使用者..."); return "ok"; } } 複製程式碼

編寫許可權檢查AOP

定義切點

    /**
     * 定義一個切點
     *
     * 通過匹配註解的方式切進來,所有被GetMapping 註解的方法都會通過這個切點進來
     *
     */
    @Pointcut(value = "@annotation(org.springframework.web.bind.annotation.GetMapping)"
) public void permissionCut(){} 複製程式碼

實現切面邏輯

目標方法前後增強方法

    @Around(value = "permissionCut()")
    public Object checkPermission(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("@Around:開始校驗許可權...");
        Signature sig = joinPoint.getSignature();
        MethodSignature msig = null;
        if (!(sig instanceof MethodSignature)) {
            throw new IllegalArgumentException("該註解只能用於方法");
        }
        msig = (MethodSignature) sig;
        Method currentMethod = msig.getMethod();


        //獲取GetMapping註解中的路徑
        String methodMapping = null;
        GetMapping methodGetMapping = currentMethod.getAnnotation(GetMapping.class);
        if(methodGetMapping!=null&&methodGetMapping.value()!=null&&methodGetMapping.value().length>0) {
            methodMapping = methodGetMapping.value()[0];
        }

        //訪問目標方法的引數:
        String userType = "";
        Object[] args = joinPoint.getArgs();
        if (args != null && args.length > 0 && args[0].getClass() == String.class) {
            userType = (String) args[0];
        }

        //刪除介面需要驗證管理員許可權
        if ("del".equals(methodMapping)){
            if (userType.equals("admin")) {
                //執行目標方法
                joinPoint.proceed();
                System.out.println("@Around:滿足許可權...");
                return "ok";
            }else {
                System.out.println("@Around:不滿足許可權...");
                return "無許可權訪問";
            }
        }
            return joinPoint.proceed();

    }
複製程式碼

其他增強方法

@Before("permissionCut()")
    public void permissionCheck(JoinPoint point) {
        System.out.println("@Before:開始許可權檢查...");
        System.out.println("@Before:目標方法為:" +
                point.getSignature().getDeclaringTypeName() +
                "." + point.getSignature().getName());
        System.out.println("@Before:引數為:" + Arrays.toString(point.getArgs()));
        System.out.println("@Before:被織入的目標物件為:" + point.getTarget());
    }

    @AfterReturning(pointcut="permissionCut()",returning="returnValue")
    public void log(JoinPoint point,Object returnValue) {
        System.out.println("@AfterReturning:開始記錄返回值...");
        System.out.println("@AfterReturning:目標方法為:" +
                point.getSignature().getDeclaringTypeName() +
                "." + point.getSignature().getName());
        System.out.println("@AfterReturning:引數為:" +
                Arrays.toString(point.getArgs()));
        System.out.println("@AfterReturning:返回值為:" + returnValue);
        System.out.println("@AfterReturning:被織入的目標物件為:" + point.getTarget());

    }

    @After("permissionCut()")
    public void releaseResource(JoinPoint point) {
        System.out.println("@Before:結束許可權檢查...");
    }
複製程式碼

完整專案程式碼

github.com/domain9065/…