SpringBoot搭配AOP實現(xiàn)自定義注解
1.springBoot的依賴
確定項目中包含可以注解的依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
2.自定義注解的步驟
在項目中自定義注解的步驟主要有兩步,第一步:定義注解類,第二步:定義切面
2.1定義注解類
直接創(chuàng)建 @interface的類,使用注解@Target和 @Retention指定其適用范圍及保留時長,如下:
@Target(ElementType.METHOD) // 指定注解的適用范圍 @Retention(RetentionPolicy.RUNTIME) //指定運(yùn)行時 public @interface ApiLog { String desc() default ""; boolean timeSpan() default true; }
注解類的內(nèi)容一般很簡單,類似于Enum類一樣,里面是簡單的方法及屬性
2.2定義切面
通過@Aspect注解指定一個類,該類必須實現(xiàn)
@Component @Aspect @Slf4j(topic = "ApiLogNote") public class ElasticSearchExecuteLog { @Around("@annotation(com.gcc.ApiLog)") public Object aroundAdvice(ProceedingJoinPoint point) throws Throwable { MethodSignature signature = (MethodSignature) point.getSignature(); //獲取被調(diào)用方法 Method method = signature.getMethod(); //取出被調(diào)用方法的注解,方便后續(xù)使用注解中的屬性 ApiLog loglinstener = method.getAnnotation(ApiLog.class); log.info("----------------------method[{}]start--------------------",method.getName()); log.info("方法描述:{}",loglinstener.desc()); log.info("參數(shù) :{}",point.getArgs()); long startTime = System.currentTimeMillis(); Object proceed = point.proceed(); long endTime = System.currentTimeMillis(); log.info("耗時:{}ss",endTime-startTime); log.info("----------------------method[{}] end--------------------\n",method.getName()) return proceed; } }
2.3使用注解
因為此例子使用的類型為METHOD即方法級的注解,直接在方法上使用即可:
@ApiLog public JSONObject seachEsData(String indexName, SearchSourceBuilder searchSourceBuilder) { JSONObject resultMap = new JSONObject(); ....... return resultMap; }
3.知識點補(bǔ)充
3.1 關(guān)于Target注解補(bǔ)充
注解@Target常常配合枚舉類ElementType來指定注解的作用位置,也叫合法位置,即你定義了一個注解,這個注解是類注解還是方法注解還是XX注解等,具體作用的范圍,取決于@Target({ElementType.TYPE})中,ElementType的枚舉值,在進(jìn)行自定義枚舉時,根據(jù)自己的需求,決定定義的注解是哪類層級使用的注解,例如上面的例子中,@ApiLog這個自定義的注解就是方法級的注解
ElementType的枚舉值有
枚舉值 | 含義 |
---|---|
TYPE | 類, 接口 (包括注解類型), 或 枚舉 聲明 |
FIELD | 字段、包括枚舉常量 |
METHOD | 方法聲明 |
PARAMETER | 正式的參數(shù)聲明 |
CONSTRUCTOR | 構(gòu)造函數(shù)的聲明 |
LOCAL_VARIABLE | 局部變量的聲明 |
ANNOTATION_TYPE | 注解類型的聲明 |
PACKAGE | 包聲明 |
3.2 關(guān)于Retention注解補(bǔ)充
注解@Retention常常配合枚舉類RetentionPolic來指定注解的各種策略,注解的保留時間,也就是何時生效,即你定義了一個注解,這個注解是編譯時生效還是僅僅只是在代碼中標(biāo)記等等,具體作用的范圍,取決于@Retention({RetentionPolic.TYPE})中,RetentionPolic的枚舉值,在進(jìn)行自定義枚舉時,大多數(shù)都是使用RUNTIME(編譯時生效)
RetentionPolic的枚舉值
枚舉值 | 含義 |
---|---|
SOURCE | 解只在源代碼級別保留,編譯時被忽略 |
CLASS | 注解將被編譯器在類文件中記錄 , 但在運(yùn)行時不需要JVM保留。這是默認(rèn)的 |
RUNTIME | 注解將被編譯器記錄在類文件中,在運(yùn)行時保留VM,也是使用最多的(一般自定義均使用這個) |
3.3 關(guān)于AOP的一些概念補(bǔ)充
這種在運(yùn)行時,動態(tài)地將代碼切入到類的指定方法、指定位置上的編程思想就是面向切面的編程
切面
切面是一個橫切關(guān)注點的模塊化,一個切面能夠包含同一個類型的不同增強(qiáng)方法,比如說事務(wù)處理和日志處理可以理解為兩個切面。切面由切入點和通知組成,它既包含了橫切邏輯的定義,也包括了切入點的定義。 Spring AOP就是負(fù)責(zé)實施切面的框架,它將切面所定義的橫切邏輯織入到切面所指定的連接點中。簡單點理解,在SpringBoot中使用了Aspect注解的類就是切面
@Component @Aspect public class LogAspect { }
目標(biāo)對象
目標(biāo)對象指將要被增強(qiáng)的對象,即包含主業(yè)務(wù)邏輯的類對象?;蛘哒f是被一個或者多個切面所通知的對象。
在我們的例子中,即是使用了@ApiLog注解的地方
連接點
程序執(zhí)行過程中明確的點,如方法的調(diào)用或特定的異常被拋出。連接點由兩個信息確定:
方法(表示程序執(zhí)行點,即在哪個目標(biāo)方法)
相對點(表示方位,即目標(biāo)方法的什么位置,比如調(diào)用前,后等)
簡單來說,連接點就是被攔截到的程序執(zhí)行點,因為Spring只支持方法類型的連接點,所以在Spring中連接點就是被攔截到的方法。
切入點
切入點是對連接點進(jìn)行攔截的條件定義。切入點表達(dá)式如何和連接點匹配是AOP的核心,Spring缺省使用AspectJ切入點語法。 一般認(rèn)為,所有的方法都可以認(rèn)為是連接點,但是我們并不希望在所有的方法上都添加通知,而切入點的作用就是提供一組規(guī)則(使用 AspectJ pointcut expression language 來描述) 來匹配連接點,給滿足規(guī)則的連接點添加通知。
//此處的匹配規(guī)則是 com.remcarpediem.test.aop.service包下的所有類的所有函數(shù)。 @Pointcut("execution(* com.remcarpediem.test.aop.service..*(..))") public void pointcut() { }
這里切入點的概念其實就是確定對哪些目標(biāo)對象進(jìn)行切面插入功能,一開始的例子是采用注解的方式來達(dá)到切入**點的作用
@Around("@annotation(com.gcc.ApiLog)")
通知
通知是指攔截到連接點之后要執(zhí)行的代碼,包括了“around”、“before”和“after”等不同類型的通知。Spring AOP框架以攔截器來實現(xiàn)通知模型,并維護(hù)一個以連接點為中心的攔截器鏈。
// @Before說明這是一個前置通知,log函數(shù)中是要前置執(zhí)行的代碼,JoinPoint是連接點, @Before("pointcut()") public void log(JoinPoint joinPoint) { } ???????//@After 為后置通知 //@Around 為環(huán)繞通知
織入(Weaving)
這里的織入概念是個動作,即Spring將前面的切面、連接點、切入點關(guān)聯(lián)起來并創(chuàng)建通知代理的過程??椚肟梢栽诰幾g時,類加載時和運(yùn)行時完成。在編譯時進(jìn)行織入就是靜態(tài)代理,而在運(yùn)行時進(jìn)行織入則是動態(tài)代理。
增強(qiáng)器(Advisor)
Advisor是切面的另外一種實現(xiàn),能夠?qū)⑼ㄖ愿鼮閺?fù)雜的方式織入到目標(biāo)對象中,是將通知包裝為更復(fù)雜切面的裝配器。Advisor由切入點和Advice組成。 Advisor這個概念來自于Spring對AOP的支撐,在AspectJ中是沒有等價的概念的。Advisor就像是一個小的自包含的切面,這個切面只有一個通知。切面自身通過一個Bean表示,并且必須實現(xiàn)一個默認(rèn)接口。
簡單來講,整個 aspect 可以描述為: 滿足 pointcut 規(guī)則的 joinpoint 會被添加相應(yīng)的 advice 操作。
將上方通過注解使用切面的方式改寫一下:
@Component @Aspect @Sl4fj public class ElasticSearchExecuteLog { // 不使用注解,而通過基礎(chǔ)的規(guī)則配置選擇切入點,表達(dá)式是指com.gcc.controller // 包下的所有類的所有方法 @Pointcut("execution(* com.gcc.controller..*(..))") public void aspect() {} // 通知,在符合aspect切入點的方法前插入如下代碼,并且將連接點作為參數(shù)傳遞 @Before("aspect()") public void log(JoinPoint joinPoint) { //連接點作為參數(shù)傳入 // 獲得類名,方法名,參數(shù)和參數(shù)名稱。 Signature signature = joinPoint.getSignature(); String className = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); Object[] arguments = joinPoint.getArgs(); MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); String[] argumentNames = methodSignature.getParameterNames(); StringBuilder sb = new StringBuilder(className + "." + methodName + "("); for (int i = 0; i< arguments.length; i++) { Object argument = arguments[i]; sb.append(argumentNames[i] + "->"); sb.append(argument != null ? argument.toString() : "null "); } sb.append(")"); log.info(sb.toString()); } }
3.4關(guān)于AOP中一些類及函數(shù)的使用
JoinPoint對象
JoinPoint對象封裝了SpringAop中切面方法的信息,在切面方法中添加JoinPoint參數(shù),就可以獲取到封裝了該方法信息的JoinPoint對象.
方法 | 作用 | 返回對象 |
---|---|---|
getSignature() | 獲取封裝了署名信息的對象,在該對象中可以獲取到目標(biāo)方法名,所屬類的Class等信息 | Signature |
getArgs() | 獲取 獲取傳入目標(biāo)方法的參數(shù)對象 | Object[] |
getTarget() | 獲取被代理的對象 | Object |
proceedingJoinPoin對象
proceedingJoinPoin對象是JoinPoint的子類,在原本JoinPoint的基礎(chǔ)上,放開了Proceeed()的使用,一般在環(huán)繞通知@Around時使用:
Object proceed() throws Throwable //執(zhí)行目標(biāo)方法 Object proceed(Object[] var1) throws Throwable //傳入的新的參數(shù)去執(zhí)行目標(biāo)方法
以上就是SpringBoot搭配AOP實現(xiàn)自定義注解的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot AOP自定義注解的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java實現(xiàn)發(fā)送短信驗證碼+redis限制發(fā)送的次數(shù)功能
這篇文章主要介紹了Java實現(xiàn)發(fā)送短信驗證碼+redis限制發(fā)送的次數(shù),本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-04-04Java編程中靜態(tài)內(nèi)部類與同步類的寫法示例
這篇文章主要介紹了Java編程中靜態(tài)內(nèi)部類與同步類的寫法示例,用于構(gòu)建靜態(tài)對象以及實現(xiàn)線程同步等,需要的朋友可以參考下2015-09-09SpringBoot使用?Sleuth?進(jìn)行分布式跟蹤的過程分析
Spring Boot Sleuth是一個分布式跟蹤解決方案,它可以幫助您在分布式系統(tǒng)中跟蹤請求并分析性能問題,Spring Boot Sleuth是Spring Cloud的一部分,它提供了分布式跟蹤的功能,本文將介紹如何在Spring Boot應(yīng)用程序中使用Sleuth進(jìn)行分布式跟蹤,感興趣的朋友一起看看吧2023-10-10java基礎(chǔ)篇之Date類型最常用的時間計算(相當(dāng)全面)
這篇文章主要給大家介紹了關(guān)于java基礎(chǔ)篇之Date類型最常用的時間計算的相關(guān)資料,Java中的Date類是用來表示日期和時間的類,它提供了一些常用的方法來處理日期和時間的操作,需要的朋友可以參考下2023-12-12Java操作mongodb增刪改查的基本操作實戰(zhàn)指南
MongoDB是一個基于分布式文件存儲的數(shù)據(jù)庫,由c++語言編寫,旨在為WEB應(yīng)用提供可擴(kuò)展的高性能數(shù)據(jù)存儲解決方案,下面這篇文章主要給大家介紹了關(guān)于Java操作mongodb增刪改查的基本操作實戰(zhàn)指南,需要的朋友可以參考下2023-05-05