spring的aop實現(xiàn)與自定義Advisor

http://www.itdecent.cn/p/b114eebcd2e9

重要概念:

Aspect(切面):
切面由切點與通知組成,可以看出是具有相同類型功能的一個類,比如日志輸出與事務管理,就可以看成是兩個切面。

Joint point(連接點):
連接點就是一些需要增強方法,spring也只支持方法類型的連接點,spring也只能對方法進行增強。

Pointcut(切點):
表示一組 joint point(因為按照功能分,可以分為很多切面,而每個切面并不是對所有的連接點都進行服務,所以按功能將一些連接點組合起來,這些就是某個切面的切點,不同切面的功能不一樣,所以連接點組合不一樣,切點也就不一樣),這些 joint point 或是通過邏輯關系組合起來,或是通過通配、正則表達式等方式集中起來,切面切入的地方。

Advice(通知):
Advice 定義了在 Pointcut 里面定義的連接點具體要做的操作,它通過 before、after 和 around 來區(qū)別是在每個 joint point 之前、之后還是環(huán)繞執(zhí)行。

Target(目標對象):
織入 Advice 的目標對象.。可以理解為一個bean,一個被代理對象。

Weaving(織入):
將切面和目標對象連接起來, 并創(chuàng)建代理對象的過程。spring的織入可以在編譯的時候,也可以在運行的時候,編譯的時候織入就是靜態(tài)代理,運行的時候織入就是動態(tài)代理。

Advisor(顧問):
Advisor是切面的另外一種實現(xiàn),能夠將通知以更為復雜的方式織入到目標對象中,是將通知包裝為更復雜切面的裝配器。Advisor由切點和Advice組成。與Aspect不同的就是切面可以有很多通知與切點,而顧問只有一個切點與通知。

基于注解的方式實現(xiàn)aop

@Component
@Aspect
public class MyTestAspect {

    //需要注意切點必須配置在切面里面(用@Aspect修飾的類里面)
    @Pointcut("execution(* com.dahuici.zyb.service.impl.UserServiceImpl.save(..))")
    public void pointcut(){}

    //配置前置通知
    @Before("pointcut()")
    public void before(){
        System.out.println("前置通知");
    }

    //配置后置通知
    @After("pointcut()")
    public void after(){
        System.out.println("后置通知");
    }

}

測試與輸出

@Test
    public void testLamda(){
        //該userService是一個bean
        userService.save();
    }
image.png

spring的aop原理(個人查看博客后的理解):spring在加載bean的時候,會判斷該bean是否是切面(用了@Aspect),如果是切面,它會將切面分解成一個一個的(顧問)Advisor,然后在創(chuàng)建號所有的bean后會用這些advisor對bean進行判斷是否滿足切點條件,如果滿足,就將advice通知注入進去(實際就是創(chuàng)建一個代理對象來替換掉原來的bean,然后就實現(xiàn)了對bean的aop增強)。需要注意的是,默認情況下,自動注入切面創(chuàng)建代理bean這個功能是關閉的,需要手動打開

自定義Advisor

首先必須知道幾個對象:Advice,PointCut,MethodMatcher,Advisor的關系

Advisor(顧問)由Advice(通知)與PointCut(切點)組成,Advice(通知)用于執(zhí)行具體需要增強的邏輯,切點用于設置那些方法需要增強,而PointCut(切點)是通過MethodMatcher對象篩選出那些類與方法需要增強。

Advisor

package com.dahuici.zyb.advisor;

import org.aopalliance.aop.Advice;
import org.springframework.aop.Pointcut;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.stereotype.Component;

@Component
public class MyAdvisor implements PointcutAdvisor {

    @Override
    //使用自定義的切點
    public Pointcut getPointcut() {
        return new MyPointCut();
    }

    @Override
    //使用自定義的前置增強
    public Advice getAdvice() {
        return new MyBeforeAdvice();
    }

    //目前該方法作用是啥不知道
    @Override
    public boolean isPerInstance() {
        return true;
    }


}

切點

package com.dahuici.zyb.advisor;

import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;

public class MyPointCut implements Pointcut {

    //用于過濾類,先過濾類,如果類不滿足,
    // 那么就不會判斷方法是否匹配
    @Override
    public ClassFilter getClassFilter() {
        //這里返回ClassFilter.TRUE表示所有類都匹配
        return ClassFilter.TRUE;
    }

    @Override
    //用于過濾方法,需要一個MyMethodMatcher用于方法邏輯判斷
    public MethodMatcher getMethodMatcher() {
        return new MyMethodMatcher();
    }

}

切點的方法匹配器MethodMatcher

package com.dahuici.zyb.advisor;

import org.springframework.aop.MethodMatcher;

import java.lang.reflect.Method;

public class MyMethodMatcher implements MethodMatcher {

    @Override
    //用得最多的是這個方法,另外兩個方法不知干啥用的,暫時就都返回false
    //判斷該方法上有@MyAnnotation
    public boolean matches(Method method, Class<?> aClass) {

        //注意自定義注解必須使用內(nèi)置注解@Retention(RetentionPolicy.RUNTIME)
        //不然獲取不了
        MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
        //判斷方法是否使用了MyAnnotation注解。
        if (annotation != null) return true;
        return false;
    }

    @Override
    public boolean isRuntime() {
        return false;
    }

    @Override
    public boolean matches(Method method, Class<?> aClass, Object... objects) {
        return false;
    }
}

自定義前置通知

package com.dahuici.zyb.advisor;

import com.dahuici.zyb.controller.MyTestController;
import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class MyBeforeAdvice implements MethodBeforeAdvice {

    /*
    * 參數(shù):
    * method-當前執(zhí)行的方法
    * objects-當前執(zhí)行方法的參數(shù)數(shù)組(這里可以對參數(shù)進行修改)
    * o-當前執(zhí)行的方法的實例對象
    *
    * */
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("我的前置通知");

    }

}

自定義注解

package com.dahuici.zyb.advisor;

import java.lang.annotation.*;

//@Target(ElementType.METHOD)
//使用該注解設置為RetentionPolicy.RUNTIME
// 才能通過反射訪問到
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation {

    String value() default "";

}

配置spring自動掃描Advisor并創(chuàng)建代理
這一步非常重要,如果不添加該bean,那么spring掃描到Advisor后也不會創(chuàng)建代理對象替換掉原來的bean。

    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
        defaultAAP.setProxyTargetClass(true);
        return defaultAAP;
    }

給測試方法添加注解并測試

    @GetMapping("/isLogin")
    @MyAnnotation
    public String isLogin(){
        System.out.println("已經(jīng)登錄");
        return "isLogin";
    }

測試結果:


image.png

shiro的啟用注解授權大概就是使用的這種方式,也是通過Advisor代理來實現(xiàn)注解授權和角色驗證的。除了以上的前置通知以外,還有后置通知于環(huán)繞通知,實現(xiàn)方式如下。

后置通知

package com.dahuici.zyb.advisor;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

public class MyAfterAdvice implements AfterReturningAdvice {
    /*
     * 參數(shù):
     * method-當前執(zhí)行的方法
     * objects-當前執(zhí)行方法的參數(shù)數(shù)組(這里可以對參數(shù)進行修改)
     * o-代理方法的返回值對象
     * o1-代理的目標對象,注意是目標對象,不是代理對象
     *
     * */
    @Override
    public void afterReturning(Object o, Method method, 
                                Object[] objects, Object o1) throws Throwable {
        System.out.println("后置通知"+o1);

    }
}

環(huán)繞通知

package com.dahuici.zyb.advisor;


import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class MyAroundAdvice implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("前");
        //執(zhí)行目標方法,proceed為目標方法的返回值
        
        Object proceed = methodInvocation.proceed();
        System.out.println(proceed);

        System.out.println("后");
        return proceed;
    }
}


?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容