前言
我們無論開發什么應用,其中都會有一個功能需求——記錄操作日志,有了操作日志的記錄既保證應用的完成性,也可以在因為誤操作而出現系統崩潰的情況下通過操作日志進行溯源,可以說記錄操作日志的功能在任何一款應用軟體中都是不可或缺的,那么各位小伙伴可以想一下,如果我們要實作記錄操作日志的功能,我們該怎么實作呢?
最簡單粗暴的辦法就是在每一個方法里增加一行代碼來記錄本次操作(插入操作日志表,本質就是一條 insert 陳述句),這樣雖然可以實作我們所需要的功能,但是實作程序就比較麻煩了,而且容易出錯誤,這時候如果使用自定義注解的話就會方便很多,很大程度上簡化了我們的代碼,而且讓代碼可讀性更強,👍
那么今天就和大家分享一下如何利用自定義注解實作“記錄操作日志”的功能🤞
利用自定義注解實作操作日志的記錄
實作自定義注解
我們還是先引入 Maven 依賴👇
<!-- SpringBoot 攔截器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
接下來我們實作一個注解類(注解在 Java 中與類、介面的宣告類似,只是所使用的關鍵字有所不同,宣告注解使用 @interface 關鍵字,在底層實作上,所有定義的注解都會自動繼承 java.lang.annotation.Annotation 介面,)
import java.lang.annotation.*;
/**
* 自定義注解-實作操作日志記錄
* 此處代碼僅作參考
* 實際開發程序中根據自己的業務功能添加所需要的欄位
*
* @description: Log
* @author: 莊霸.liziye
* @create: 2021-12-21 12:47
**/
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log
{
/**
* 模塊
*/
public String title() default "";
/**
* 操作型別
*
* 此處的 BusinessType 是我自己定義的列舉類
* BusinessType.OTHER 代表其他操作
*/
public BusinessType businessType() default BusinessType.OTHER;
}
關于注解的定義,咱們多說幾句😁
① 訪問修飾符必須為 public,不寫的話則默認為 public,
② 自定義注解中元素的型別只能是基本資料型別、String、Class、列舉型別,也可以是注解型別(體現了注解的嵌套效果)以及上述型別的一位陣列,
③ 自定義注解中的 default 代表默認值,
④ 自定義注解上的 @Target 注解的作用是限定自定義注解能夠被應用在哪些Java 元素上,ElementType 是一個列舉型別,其原始碼如下👇
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD,
/** Formal parameter declaration */
PARAMETER,
/** Constructor declaration */
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE
}
⑤ 自定義注解上的 @Retention 注解用來定義其宣告周期,注解的生命周期有三個階段,分別是:源檔案階段;編譯階段;運行階段,RetentionPolicy 同樣是一個列舉型別,其原始碼如下👇
ublic enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
⑥ 自定義注解上的 @Documented 是一個標記注解,表明該自定義注解應該被 JavaDoc 工具記錄.,默認情況下 JavaDoc 的記錄是不包括注解的, 但如果宣告注解時指定了 @Documented ,該注解就會被 JavaDoc 之類的工具處理,所以注解型別資訊也會被生成到 JavaDoc 檔案中,
下面我們就需要定義一個切面類,來進一步的實作自定義注解的功能,代碼如下👇(這里就只貼一下重要代碼)
/**
* 自定義注解-操作日志記錄處理
* 此處代碼僅作參考,開發程序中根據自己的業務功能需求添加所需要業務邏輯
*
* @description: Log
* @author: 莊霸.liziye
* @create: 2021-12-22 13:07
**/
@Aspect
@Component
public class LogAspect
{
private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
// 配置織入點-xxx代表自定義注解的存放位置,如:com.test.annotation.Log
@Pointcut("@annotation(xxxx)")
public void logPointCut()
{
}
/**
* 處理完請求后執行此處代碼
*
* @param joinPoint 切點
*/
@AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult)
{
handleLog(joinPoint, controllerLog, null, jsonResult);
}
/**
* 如果處理請求時出現例外,在拋出例外后執行此處代碼
*
* @param joinPoint 切點
* @param e 例外
*/
@AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e)
{
handleLog(joinPoint, controllerLog, e, null);
}
/**
* 日志處理
*/
protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult)
{
try
{
//此處為處理日志的具體業務邏輯
//最終將操作資訊插入至操作日志表
}
catch (Exception exp)
{
// 記錄本地例外日志
log.error("**** 出現例外 ****");
log.error("例外資訊:{}", exp.getMessage());
exp.printStackTrace();
}
}
}
這里咱們主要解釋一下切面類中的各個注解的作用:
① @Aspect:一個標識性的注解,表示當前類是一個切面,可以被容器讀取,
② @Component:表示該類被 Spring 管理,可以理解為將該類注入進 Spring(就像向 Spring 注入 bean 一樣),
③ @Pointcut:Pointcut是植入切面的觸發條件,每個 Pointcut 的定義包括2部分,一是運算式,二是方法簽名,方法簽名必須是 public 及 void,可以將Pointcut 中的方法看作是一個被切面參考的標記符號,因為運算式不直觀,因此我們可以通過方法簽名的方式為此運算式命名,因此 Pointcut 中的方法只需要方法簽名,而不需要在方法體內撰寫實際代碼,
④ @AfterReturning:后置增強,相當于AfterReturningAdvice,給方法增加該注解后表示當方法正常退出時執行此方法,
⑤ @AfterThrowing:例外拋出增強,相當于ThrowsAdvice,給方法增加該注解后表示當方法拋出例外時執行此方法,
⑥ pointcut/value 這兩個屬性的作用是一樣的,它們都屬于指定切入點對應的切入運算式,一樣既可以是已有的切入點,也可直接定義切入點運算式,當指定了 pointcut 屬性值后,value 屬性值將會被覆寫,
⑦ returning:該屬性指定一個形參名,用于表示方法中可定義與此同名的形參,該形參可用于訪問目標方法的回傳值,需要注意的是,在方法中定義該形參時指定了型別,則限制目標方法必須回傳此型別的值或沒有回傳值,
至此,我們的自定義注解就配置完成了,最后我們再來看一下如何使用自定義注解~
使用自定義注解
使用自定義注解就很簡單了,使用方式和其他注解是相同的👇

只要我們在需要記錄操作日志的方法上增加配置好的自定義注解(別忘了給自定義注解中的欄位賦值哦~)就可以使用啦,我們每次請求方法時,都會幫助我們記錄本次操作,

P.S. 雖然定義自定義注解的程序麻煩了一些,但是磨刀不誤砍柴工,合理的使用自定義注解不僅僅提升了編碼效率,而且可以瞬間提升代碼的逼格💪
小結
本人經驗有限,有些地方可能講的沒有特別到位,如果您在閱讀的時候想到了什么問題,歡迎在評論區留言,我們后續再一一探討🙇?
希望各位小伙伴動動自己可愛的小手,來一波點贊+關注 (????) 讓更多小伙伴看到這篇文章~ 蟹蟹呦(●’?’●)
如果文章中有錯誤,歡迎大家留言指正;若您有更好、更獨到的理解,歡迎您在留言區留下您的寶貴想法,
你在被打擊時,記起你的珍貴,抵抗惡意;
你在迷茫時,堅信你的珍貴,拋開蜚語;
愛你所愛 行你所行 聽從你心 無問東西
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/390650.html
標籤:java
