用AOP攔截自定義注解并獲取注解屬性與上下文參數(shù)(基于Springboot框架)

[TOC]

AOP可以用于日志的設(shè)計,這樣話就少不了要獲取上下文的信息,博主在設(shè)計日志模塊時考慮了一下此法蒿囤,整理了一下如何用AOP來攔截你自定義的注解。

自定義注解

首先先自定義一個注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Axin {
    /**
     * 所屬模塊
     * @return
     */
    String module()  default "日志模塊";

    /**
     * 動作描述
     * @return
     */
    String desc()  default "無動作";
}
  • @Documented:注解表明制作javadoc時崇决,是否將注解信息加入文檔材诽。如果注解在聲明時使用了@Documented底挫,則在制作javadoc時注解信息會加入javadoc。
  • @Target:用來說明該注解可以被聲明在那些元素之前
    • @Target(ElementType.TYPE) //接口脸侥、類建邓、枚舉、注解
    • @Target(ElementType.FIELD) //字段睁枕、枚舉的常量
    • @Target(ElementType.METHOD) //方法
    • @Target(ElementType.PARAMETER) //方法參數(shù)
    • @Target(ElementType.CONSTRUCTOR) //構(gòu)造函數(shù)
    • @Target(ElementType.LOCAL_VARIABLE)//局部變量
    • @Target(ElementType.ANNOTATION_TYPE)//注解
    • @Target(ElementType.PACKAGE) ///包
  • @Retention:用來說明該注解類的生命周期官边。
    • @Retention(RetentionPolicy.SOURCE) —— 這種類型的Annotations只在源代碼級別保留,編譯時就會被忽略
    • @Retention(RetentionPolicy.CLASS) —— 這種類型的Annotations編譯時被保留,在class文件中存在,但JVM將會忽略
    • @Retention(RetentionPolicy.RUNTIME) —— 這種類型的Annotations將被JVM保留,所以他們能在運(yùn)行時被JVM或其他使用反射機(jī)制的代碼所讀取和使用.

定義切面

/**
 * @author Axin
 */
@Aspect
@Component
public class AxinAspect {

    /**
     * 這里定義了一個總的匹配規(guī)則,以后攔截的時候直接攔截log()方法即可外遇,無須去重復(fù)寫execution表達(dá)式
     */
    @Pointcut("@annotation(Axin)")
    public void log() {
    }

    @Before("log()&&@annotation(axin)")
    public void doBefore(JoinPoint joinPoint,Axin axin) {
        System.out.println("******攔截前的邏輯******");
        System.out.println("目標(biāo)方法名為:" + joinPoint.getSignature().getName());
        System.out.println("目標(biāo)方法所屬類的簡單類名:" + joinPoint.getSignature().getDeclaringType().getSimpleName());
        System.out.println("目標(biāo)方法所屬類的類名:" + joinPoint.getSignature().getDeclaringTypeName());
        System.out.println("目標(biāo)方法聲明類型:" + Modifier.toString(joinPoint.getSignature().getModifiers()));
        //獲取傳入目標(biāo)方法的參數(shù)
        Object[] args = joinPoint.getArgs();
        for (int i = 0; i < args.length; i++) {
            System.out.println("第" + (i + 1) + "個參數(shù)為:" + args[i]);
        }
        System.out.println("被代理的對象:" + joinPoint.getTarget());
        System.out.println("代理對象自己:" + joinPoint.getThis());

        System.out.println("攔截的注解的參數(shù):");
        System.out.println(axin.module());
        System.out.println(axin.desc());
    }

    @Around("log()&&@annotation(axin)")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint,Axin axin) throws Throwable {
        System.out.println("環(huán)繞通知:");
        System.out.println(axin.module());
        System.out.println(axin.desc());
        Object result = null;
        result = proceedingJoinPoint.proceed();
        return result;
    }

    @After("log()")
    public void doAfter() {
        System.out.println("******攔截后的邏輯******");
    }
}

匹配規(guī)則

//匹配AOP對象的目標(biāo)對象為指定類型的方法注簿,即DemoDao的aop的代理對象
@Pointcut("this(com.hhu.DemaoDao)")
public void thisDemo() {
    ...
}

通知類別

  • 前置通知(Before advice)- 在目標(biāo)方便調(diào)用前執(zhí)行通知
  • 后置通知(After advice)- 在目標(biāo)方法完成后執(zhí)行通知
  • 返回通知(After returning advice)- 在目標(biāo)方法執(zhí)行成功后,調(diào)用通知
  • 異常通知(After throwing advice)- 在目標(biāo)方法拋出異常后跳仿,執(zhí)行通知
  • 環(huán)繞通知(Around advice)- 在目標(biāo)方法調(diào)用前后均可執(zhí)行自定義邏輯

獲取上下文信息JoinPoint

JoinPoint對象封裝了SpringAop中切面方法的信息,在切面方法中添加JoinPoint參數(shù),就可以獲取到封裝了該方法信息的JoinPoint對象. 注意:這用于非環(huán)繞通知

方法名 功能
Signature getSignature(); 獲取封裝了署名信息的對象,在該對象中可以獲取到目標(biāo)方法名,所屬類的Class等信息
Object[] getArgs(); 獲取傳入目標(biāo)方法的參數(shù)對象
Object getTarget(); 獲取被代理的對象
Object getThis(); 獲取代理對象

方法使用模板:

public void doBefore(JoinPoint joinPoint) {
System.out.println("******攔截前的邏輯******");
System.out.println("目標(biāo)方法名為:" + joinPoint.getSignature().getName());
System.out.println("目標(biāo)方法所屬類的簡單類名:" + joinPoint.getSignature().getDeclaringType().getSimpleName());
    System.out.println("目標(biāo)方法所屬類的類名:" + joinPoint.getSignature().getDeclaringTypeName());
    System.out.println("目標(biāo)方法聲明類型:" + Modifier.toString(joinPoint.getSignature().getModifiers()));
    //獲取傳入目標(biāo)方法的參數(shù)
    Object[] args = joinPoint.getArgs();
    for (int i = 0; i < args.length; i++) {
        System.out.println("第" + (i + 1) + "個參數(shù)為:" + args[i]);
    }
    System.out.println("被代理的對象:" + joinPoint.getTarget());
    System.out.println("代理對象自己:" + joinPoint.getThis());
}

ProceedingJoinPoint

ProceedingJoinPoint對象是JoinPoint的子接口,該對象只用在@Around的切面方法中

方法名 功能
Object proceed() throws Throwable 執(zhí)行目標(biāo)方法
Object proceed(Object[] var1) throws Throwable 傳入的新的參數(shù)去執(zhí)行目標(biāo)方法

定義測試方法

//Service接口
public interface AxinService {
    String axinRun(String arg1, User user);
}

//實現(xiàn)類
/**
 * @author Axin
 */
@Component
public class AxinServiceImpl implements AxinService {

    @Axin(module = "print",desc = "打印")
    @Override
    public String axinRun(String arg1, User user) {
        String res = arg1 + user.getName() + user.getAge();
        return res;
    }

    public String axinRun(String arg1, Person person) {
        String res = arg1 + person.getName() + person.getAge();
        return res;
    }
}

//控制類
/**
 * @author Axin
 */
@RestController
public class HelloController {
    @Autowired
    AxinService axinService;

    @RequestMapping("/hello")
    public String hello() {
        User user = new User();
        user.setAge(10);
        user.setName("張三");
        String res = axinService.axinRun("Test:", user);

        return "Hello Spring Boot!<br>"+res;
    }
}

測試結(jié)果

環(huán)繞通知:
print
打印
******攔截前的邏輯******
目標(biāo)方法名為:axinRun
目標(biāo)方法所屬類的簡單類名:AxinService
目標(biāo)方法所屬類的類名:com.axin.springboot.service.AxinService
目標(biāo)方法聲明類型:public abstract
第1個參數(shù)為:Test:
第2個參數(shù)為:User(id=null, name=張三, age=10, date=null)
被代理的對象:com.axin.springboot.service.AxinServiceImpl@ac2ddcc
代理對象自己:com.axin.springboot.service.AxinServiceImpl@ac2ddcc
攔截的注解的參數(shù):
print
打印
******攔截后的邏輯******

小結(jié)

通過上述的代碼演示诡渴,我們可以自定義一個注解,然后配置切面來攔截有注解的方法菲语,同時也可以獲得方法傳入的參數(shù)來完成你的業(yè)務(wù)需求妄辩。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市山上,隨后出現(xiàn)的幾起案子眼耀,更是在濱河造成了極大的恐慌,老刑警劉巖佩憾,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件哮伟,死亡現(xiàn)場離奇詭異,居然都是意外死亡妄帘,警方通過查閱死者的電腦和手機(jī)澈吨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來寄摆,“玉大人谅辣,你說我怎么就攤上這事∩裟眨” “怎么了桑阶?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長勾邦。 經(jīng)常有香客問我蚣录,道長,這世上最難降的妖魔是什么眷篇? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任萎河,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘虐杯。我一直安慰自己玛歌,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布擎椰。 她就那樣靜靜地躺著支子,像睡著了一般。 火紅的嫁衣襯著肌膚如雪达舒。 梳的紋絲不亂的頭發(fā)上值朋,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機(jī)與錄音巩搏,去河邊找鬼昨登。 笑死,一個胖子當(dāng)著我的面吹牛贯底,可吹牛的內(nèi)容都是我干的丰辣。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼丈甸,長吁一口氣:“原來是場噩夢啊……” “哼糯俗!你這毒婦竟也來了尿褪?” 一聲冷哼從身側(cè)響起睦擂,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎杖玲,沒想到半個月后顿仇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡摆马,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年臼闻,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片囤采。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡述呐,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蕉毯,到底是詐尸還是另有隱情乓搬,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布代虾,位于F島的核電站进肯,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏棉磨。R本人自食惡果不足惜江掩,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧环形,春花似錦策泣、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至拗军,卻和暖如春任洞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背发侵。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工交掏, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留刃鳄,地道東北人盅弛。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓叔锐,卻偏偏與公主長得像,于是被迫代替她去往敵國和親愉烙。 傳聞我的和親對象是個殘疾皇子讨盒,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容