SpringBoot自定義注解的簡單實現(xiàn)

在spring中易阳,AOP和IOC都是spring非常重要的特性,而在web開發(fā)中饵沧,定義切面锨络、增強方法也是比較常見的,比如做統(tǒng)一的日志管理相關的狼牺、自定義的注解處理羡儿、或者在處理用戶請求的前后我們需要做一些處理,等等是钥,這時我們都可以使用切面來實現(xiàn)掠归,而在以前缅叠,使用切面我們可能需要使用很多接口和類,現(xiàn)在虏冻,我們只需要@Aspect這一個注解就可以定義切面肤粱。

AOP

AOP(Aspect Oriented Programming,面向切面編程)是通過預編譯方式和運行期動態(tài)代理實現(xiàn)程序功能的統(tǒng)一維護的一種技術厨相。AOP是OOP的延續(xù)领曼,是軟件開發(fā)中的一個熱點,也是Spring框架中的一個重要內容蛮穿,是函數(shù)式編程的一種衍生范型庶骄。利用AOP可以對業(yè)務邏輯的各個部分進行隔離,從而使得業(yè)務邏輯各部分之間的耦合度降低践磅,提高程序的可重用性单刁,同時提高了開發(fā)的效率。

在Spring AOP中業(yè)務邏輯僅僅只關注業(yè)務本身音诈,將日志記錄幻碱、性能統(tǒng)計、安全控制细溅、事務處理褥傍、異常處理等代碼從業(yè)務邏輯代碼中劃分出來,從而在改變這些行為的時候不影響業(yè)務邏輯的代碼喇聊。

相關注解介紹:

注解 作用
@Aspect 把當前類標識為一個切面
@Pointcut Pointcut是織入Advice的觸發(fā)條件恍风。每個Pointcut的定義包括2部分,一是表達式誓篱,二是方法簽名朋贬。方法簽名必須是public及void型〈芙荆可以將Pointcut中的方法看作是一個被Advice引用的助記符锦募,因為表達式不直觀,因此我們可以通過方法簽名的方式為此表達式命名邻遏。因此Pointcut中的方法只需要方法簽名糠亩,而不需要在方法體內編寫實際代碼。
@Around 環(huán)繞增強准验,目標方法執(zhí)行前后分別執(zhí)行一些代碼
@AfterReturning 返回增強赎线,目標方法正常執(zhí)行完畢時執(zhí)行
@Before 前置增強,目標方法執(zhí)行之前執(zhí)行
@AfterThrowing 異常拋出增強糊饱,目標方法發(fā)生異常的時候執(zhí)行
@After 后置增強垂寥,不管是拋出異常或者正常退出都會執(zhí)行
image.png

1. pom.xml添加aop支持

<!-- 引入aop切面支持 -->
<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2. 創(chuàng)建自定義注解

@interface是用來自定義JAVA Annotation的語法,
@interface是用來自定義注釋類型的

注釋類型的定義跟定義一個接口相似滞项,我們需要在 interface這個關鍵字前面加上一個@符號狭归,即@interface。

注釋中的每一個方法定義了這個注釋類型的一個元素蓖扑,注釋中方法的聲明中一定不能包含參數(shù)唉铜,也不能拋出異常;方法的返回值被限制為簡單類型律杠、String、Class竞惋、emnus柜去、注釋,和這些類型的數(shù)組拆宛。方法可以有一個缺省值嗓奢。

注解僅支持 primitives, string和 enumerations這三種類型。 注解的所有屬性都定義為方法浑厚,也可以提供默認值股耽。

package com.philos.common.annotation;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Documented
@Retention(RUNTIME)
@Target(METHOD)
public @interface LoginAnno {
      String value();
}

上面這個注釋里面只定義了一個字符串,它的目標注釋對象是類钳幅,保留策略是在運行期間物蝙。

元注解釋義:
java.lang.annotation提供了四種元注解,專門注解其他的注解(在自定義注解的時候敢艰,需要使用到元注解):
@Documented –注解是否將包含在JavaDoc中
@Retention –什么時候使用該注解
@Target –注解用于什么地方
@Inherited – 是否允許子類繼承該注解

java在jdk5當中支持了這一功能诬乞,并且在java.lang.annotation包中提供了四個注解,僅用于編寫注解時使用钠导,他們是:

注解 作用
@Documented 表明是否在java doc中添加Annotation
@Retention 定義注釋應保留多長時間震嫉,即有效周期。有以下幾種策略: RetentionPolicy.SOURCE - 在編譯期間丟棄牡属。 編譯完成后票堵,這些注釋沒有任何意義,因此它們不會寫入字節(jié)碼逮栅。 示例@Override悴势,@ SuppressWarnings RetentionPolicy.CLASS - 在類加載期間丟棄。 在進行字節(jié)碼級后處理時很有用证芭。 有點令人驚訝的是瞳浦,這是默認值。 RetentionPolicy.RUNTIME - 不要丟棄废士。 注釋應該可以在運行時進行反射叫潦。 這是我們通常用于自定義注釋的內容。
@Target 指定可以放置注解的位置官硝。 如果不指定矗蕊,則可以將注解放在任何位置短蜕。若我們只想要其中幾個,則需要定義對應的幾個下面是這8個屬性: ElementType.TYPE(類傻咖,接口朋魔,枚舉) ElementType.FIELD(實例變量) ElementType.METHOD ElementType.PARAMETER ElementType.CONSTRUCTOR ElementType.LOCAL_VARIABLE ElementType.ANNOTATION_TYPE(在另一個注釋上) ElementType.PACKAGE(記住package-info.java)
@Inherited 控制注解是否對子類產生影響。

簡單使用注解

下面我們定義一個方法來使用這個注解:

public class UseAnnotation {
 
    @LoginAnno("testStringValue")
    public void testMethod(){
        //do something here
    }
}

我們在這里使用了這個自定義的注解@LoginAnno卿操,并把字符串賦值為:testStringValue,到這里,定義一個注解并使用它警检,我們就已經(jīng)全部完成。

3. 創(chuàng)建自定義注解解析

定義切點害淤,并對切點做一些增強操作:前置增強扇雕、環(huán)繞增強、后置增強等等窥摄,切點的定義我們可以在一個空方法體的方法上使用@Pointcut注解

package com.philos.common.annotation;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * 描述:通過@Aspect注解使該類成為切面類 
 */
@Aspect
@Component
public class LoginAnnoImpl {

    @Pointcut("@annotation(com.philos.common.annotation.LoginAnno)")
    private void cut() {
    }

    /**
     *  功能:前置通知 
     */
    @Before("cut()")
    public void before() {
        System.out.println("自定義注解生效了");
    }
}

@Pointcut() 注解

@annotation() 類或者方法

@Pointcut()里面定義的是切點表達式镶奉,切點表達式有很多,上面例子代碼中的是注解表達式崭放,標注來指定注解的目標類或者方法哨苛,就比如凡是使用了com.philos.common.annotation.LoginAnno這個注解的類或者方法都是切點。除了@annotation()還有幾類比較常見的切點表達式:

execution(方法修飾符 返回類型 方法全限定名 參數(shù)) 匹配指定的方法

@Pointcut("execution(* com.tcb.controller.SDProductController.showproductDetail(..))")
  • 匹配任意字符币砂,但只能匹配一個元素
    .. 匹配任意字符建峭,可以匹配任意多個元素,表示類時道伟,必須和*聯(lián)合使用
  • 必須跟在類名后面迹缀,如Student+,表示類本身和繼承或擴展指定類的所有類

2.args( 參數(shù)類型的類全限定名 )
匹配參數(shù)是指定類型的方法蜜徽,比如@Pointcut(com.xx.Student) 就是匹配所有參數(shù)是student類對象的方法祝懂,像void add(Student s)這樣的方法就會被匹配。

3.@args( 注解類的類全限定名 )
匹配參數(shù)的類型的類被指定注解修飾的方法拘鞋,注意砚蓬,這個參數(shù)的類型還必須是自定義的類,比如@Pointcut(com.xx.anno.ExAnno)盆色,你有一個方法void(Student s){}灰蛙,而參數(shù)類型Student是你自定義的類,而Student這個類被@ExAnno修飾了隔躲,那么摩梧,這個方法就會被匹配(這個地方有點坑!)宣旱。
4.within( 類全限定名 )
匹配指定的類仅父,比如@Pointcut(com.xx.IndexController)就是IndexController類下的方法都會被匹配,這里的類名支持正則模糊匹配,比如@Pointcut(com.xx.*Controller)就是com.xx包下的所有的Controller都會被匹配笙纤。對了耗溜,上面和下面的表達式都支持正則模糊匹配
5.target( 類全限定名 )
匹配指定的類以及它的子類,比如@Pointcut(com.xxx.dao.BaseDao)就是匹配BaseDao接口以及所有實現(xiàn)類這個接口的子類省容。
6.@within( 類全限定名 )
匹配使用了指定的注解的類以及它的子類抖拴,比如@Pointcut(com.xxx.anno.Log)就是指,我在BaseController里使用了@Log注解腥椒,那么BaseController以及繼承BaseController的類都會被匹配阿宅。
7.@target( 類全限定名 )
匹配使用了指定注解的類(從這里我們看出來,within寞酿、target和@within家夺、@target是相反,郁悶伐弹。。榨为。)惨好,還是@Pointcut(com.xxx.anno.Log)就是僅匹配使用了@Log注解的類

--

ok,以上是一些較常用的切點表達式随闺,然后繼續(xù)之前的日川。
在定義切點之后,我們就可以對切點進行增強操作(注意矩乐,我前面@Pointcut注解修飾的方法名是pointcut()哦)龄句,比如@Before("pointcut()")前置增強,@Around("pointcut()")環(huán)繞增強散罕。分歇。。等等欧漱,注意职抡,我這里直接使用的前面定義切點的那個方法名,這種方式屬于獨立切點命名(劃重點N笊酢)缚甩,就是將切點單獨定義出來,對于當切點較多時窑邦,能夠提高一些重用性擅威。這種算是顯式地定義切點,除了這種還可以使用匿名切點冈钦,即直接在增強操作方法里直接寫切點表達式郊丛,比如:

@Before("execution(* com.tcb.controller.SDProductController.showproductDetail(..))")
    public void beforeBrowse(JoinPoint joinPoint) {}

增強主要有以下幾種:
1.@Before
前置增強,在切點方法執(zhí)行之前執(zhí)行。這里多說幾句宾袜,在增強方法中要獲取request可以通過下面來獲饶硌蕖:

ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();

在前置增強中,想要獲取切點方法的參數(shù)可以通過joinPoint.getArgs[]來獲取庆猫,獲取方法名可以通過joinPoint.getSignature().getDeclaringTypeName()來獲取认轨。
2.@Around
環(huán)繞增強.
3.@AfterReturning
后置增強,切點方法正常執(zhí)行完返回后執(zhí)行月培,如果有異常拋出而退出嘁字,則不會執(zhí)行增強方法
4.@AfterThrowing
后置增強,只有切點方法異常拋出而退出后執(zhí)行
5.@After
也是后置增強杉畜,但不管切點方法是正常退出還是異常退出都會執(zhí)行

至此自定義注解就編寫完畢了纪蜒,下面來看看調用

4. 使用自定義注解

package com.qfx.common.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.qfx.common.annotation.LoginAnno;

@RestController
@RequestMapping("login")
public class LoginController {

    @RequestMapping("reg")
    public String reg(String userName) {
        return "用戶[" + userName +"]注冊成功~!";
    }


    @RequestMapping("login")
    @LoginAnno
    public String login(String userName) {
        return "歡迎您:" + userName;
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市此叠,隨后出現(xiàn)的幾起案子纯续,更是在濱河造成了極大的恐慌,老刑警劉巖灭袁,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件猬错,死亡現(xiàn)場離奇詭異,居然都是意外死亡茸歧,警方通過查閱死者的電腦和手機倦炒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來软瞎,“玉大人逢唤,你說我怎么就攤上這事〉咏剑” “怎么了鳖藕?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長芙代。 經(jīng)常有香客問我吊奢,道長,這世上最難降的妖魔是什么纹烹? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任页滚,我火速辦了婚禮,結果婚禮上铺呵,老公的妹妹穿的比我還像新娘裹驰。我一直安慰自己,他們只是感情好片挂,可當我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布幻林。 她就那樣靜靜地躺著贞盯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪沪饺。 梳的紋絲不亂的頭發(fā)上躏敢,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天,我揣著相機與錄音整葡,去河邊找鬼件余。 笑死,一個胖子當著我的面吹牛遭居,可吹牛的內容都是我干的啼器。 我是一名探鬼主播,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼俱萍,長吁一口氣:“原來是場噩夢啊……” “哼端壳!你這毒婦竟也來了?” 一聲冷哼從身側響起枪蘑,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤损谦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后岳颇,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體成翩,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年赦役,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片栅炒。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡掂摔,死狀恐怖,靈堂內的尸體忽然破棺而出赢赊,到底是詐尸還是另有隱情乙漓,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布释移,位于F島的核電站叭披,受9級特大地震影響,放射性物質發(fā)生泄漏玩讳。R本人自食惡果不足惜涩蜘,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望熏纯。 院中可真熱鬧同诫,春花似錦、人聲如沸樟澜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至霹俺,卻和暖如春柔吼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背丙唧。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工愈魏, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人艇棕。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓蝌戒,卻偏偏與公主長得像,于是被迫代替她去往敵國和親沼琉。 傳聞我的和親對象是個殘疾皇子北苟,可洞房花燭夜當晚...
    茶點故事閱讀 44,700評論 2 354