最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot 自定義注解異步記錄復(fù)雜日志詳解

 更新時(shí)間:2022年09月28日 14:58:12   作者:AKone  
這篇文章主要為大家介紹了SpringBoot 自定義注解異步記錄復(fù)雜日志詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

1、背景

最近接手一個(gè)任務(wù),需要給當(dāng)前項(xiàng)目加一個(gè)較為復(fù)雜的日志。有多復(fù)雜呢? 要有日志類型、不同日志類型要有不同的操作和備注等。作為小白的我最開始的做法是在業(yè)務(wù)層寫代碼記錄日志,好處就是方便,壞處就是這種做法直接侵襲Service層,Service層無法做到職責(zé)單一了。

經(jīng)導(dǎo)師點(diǎn)撥,改用自定義注解+AOP切面異步日志

2、技術(shù)方案-自定義注解

注解(Annotation),也叫元數(shù)據(jù)。

2.1 注解介紹

注解其實(shí)就是代碼里的特殊標(biāo)記,這些標(biāo)記可以在編譯、類加載、運(yùn)行時(shí)被讀取,并執(zhí)行相應(yīng)的處理。通過使用注解,程序員可以在不改變?cè)羞壿嫷那闆r下,在源文件中嵌入一些補(bǔ)充信息。

2.2 元注解

元注解用于修飾其他注解的注解,在JDK5.0中提供了四種元注解:Retention、Target、Documented、Inherited

(1) Retention介紹: 用于修飾注解,用于指定修飾的哪個(gè)注解的聲明周期。@Rentention包含一個(gè)RetentionPolicy枚舉類型的成員變量,使用@Rentention時(shí)必須為該value成員變量指定值

  • RetentionPolicy.SOURCE:在源文件中有效(即源文件保留),編譯器直接丟棄這種策略的注釋,在.class文件中不會(huì)保留注解信息
  • RetentionPolicy.CLASS:在class文件中有效(即class保留),保留在.class文件中,但是當(dāng)運(yùn)行java程序時(shí),他就不會(huì)繼續(xù)加載了,不會(huì)保留在內(nèi)存中,JVM不會(huì)保留注解。如果注解沒有加Retention元注解,那么相當(dāng)于默認(rèn)的注解就是這種狀態(tài)。
  • RetentionPolicy.RUNTIME:在運(yùn)行有效(即運(yùn)行時(shí)保留),當(dāng)運(yùn)行Java程序時(shí),JVM會(huì)保留注釋,加載在內(nèi)存中,那么程序可以通過反射獲取該注釋。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    RetentionPolicy value();
}
public enum RetentionPolicy {
    SOURCE,
    CLASS,
    RUNTIME
}

(2)Target 介紹: 用于修飾注解的注解,定義當(dāng)前注解能夠修飾程序中的哪些元素(類、屬性、方法,構(gòu)造器等等)

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}

(3)Documented 介紹: 用于指定被該元注解修飾的注解類將被javadoc工具提取成文檔。默認(rèn)情況下,javadoc是不包括注解的,但是加上這個(gè)注解生成的文檔中就會(huì)帶著注解了

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}

(4)Inherited 介紹:被它修飾的Annotation將具有繼承性。如果某個(gè)類使用了被@Inherited修飾的Annotation,則其子類將自動(dòng)具有該注解。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

注解的基礎(chǔ)知識(shí)基本介紹完畢,我們開始寫起來吧?。?!

2.3 實(shí)現(xiàn)自定義注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Log {
    // 日志類型
    LogType logType() ;
    // 操作類型
    OperateType operate();
    // 備注
    String note() default "";
}

3、技術(shù)方案-AOP切面

AOP切面編程一般可以幫助我們?cè)诓恍薷默F(xiàn)有代碼的情況下,對(duì)程序的功能進(jìn)行拓展, 往往用于實(shí)現(xiàn) 日志處理,權(quán)限控制,性能檢測,事務(wù)控制等。AOP實(shí)現(xiàn)的原理就是動(dòng)態(tài)代理。在有接口的情況下,使用JDK動(dòng)態(tài)代理,在沒有接口的情況下使用cglib動(dòng)態(tài)代理。

3.1 AOP術(shù)語解析

(1) Joint point:類里面那些可以被增強(qiáng)的方法,這些方法被稱之為連接點(diǎn)

(2) PointCut:實(shí)際被增強(qiáng)的方法,這些方法被稱之為連接點(diǎn)

(3) Advice:實(shí)際增加的邏輯部分稱為通知

(4) Target:被增強(qiáng)功能的對(duì)象(被代理的對(duì)象)

(5) Aspect:Aspect 聲明類似與Java中的類聲明,在Aspect中會(huì)包含著一些PointCut以及相應(yīng)的Advice.

(6) Weaving:創(chuàng)建代理對(duì)象并實(shí)現(xiàn)功能增強(qiáng)的聲明并運(yùn)行過程將Aspect和其他對(duì)象連接起來,并創(chuàng)建Adviced object的過程

3.2 切入點(diǎn)表達(dá)式

切入點(diǎn)表達(dá)式:通過一個(gè)表達(dá)式來確定AOP要增強(qiáng)的是哪個(gè)或者哪些方法.

3.3 ADVICE通知類型

(1)前置通知:@Before 執(zhí)行前置通知,并通過JointPoint獲取方法中的參數(shù)

@Aspect
@Component
public class DaoAspect {
	/*
	前置通知:在執(zhí)行addUser方法之前,執(zhí)行前置通知,并通過JointPoint獲取addUser()方法中的參數(shù)
	*/
    @Before("execution(* com.xzit.dao.Impl.UserDaoImpl.addUser(..))")
    public void methodBefore(JoinPoint joinPoint){
        System.out.println("methodBefore invoked ... ...");
        Object[] args = joinPoint.getArgs();
        System.out.println(Arrays.toString(args));
    }
}

(2)后置通知:@After 切點(diǎn)方法是否出現(xiàn)異常,后置通知都會(huì)執(zhí)行

(3)返回通知:@AfterReturning 切點(diǎn)出現(xiàn)異常,返回通知不會(huì)執(zhí)行

(4)異常通知:@AfterThrowing 切點(diǎn)方法出現(xiàn)異常就執(zhí)行,不出現(xiàn)異常就不執(zhí)行

(5)環(huán)繞通知:@Around 在切點(diǎn)方法之前和之后執(zhí)行。是@Before和@AfterReturing 相結(jié)合

3.4 技術(shù)實(shí)現(xiàn)

根據(jù)任務(wù)背景,我選擇了返回通知@AfterReturning。使用返回通知一定要注意的是:由于我需要用到返回值,所以會(huì)用@AfterReturning注解中的returning屬性來獲取方法的返回值

  • returning指定的名稱必須和切面方法參數(shù)中的名稱相同。例如在下面代碼中指定的"cId"都是相同的
@AfterReturning(pointcut = "@annotation(com.xxx.xxx.xxx.Log)",
        returning = "cId")
public void handleRdLogs(JoinPoint joinPoint, int cId) 
  • 切面方法參數(shù)中的參數(shù)類型必須和方法返回類型一致
@AfterReturning(pointcut = "@annotation(com.xxx.xxx.xxx.Log)",
        returning = "cId")
public void handleRdLogs(JoinPoint joinPoint, int cId) {
    // 獲取方法簽名
    MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
    // 獲取@Log注解內(nèi)容
    if (!methodSignature.getMethod().isAnnotationPresent(Log.class)) {
        log.error("獲取注解@Log失敗");
        throw new Exception("獲取注解@Log失敗");
    }
    Log log = methodSignature.getMethod().getAnnotation(Log.class);
    // 輸出日志的備注
    System.out.println(log.note())
}

3.5 相關(guān)操作

(1) 獲取方法簽名

MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();`

(2)根據(jù)方法簽名獲取自定義注解

Log log = methodSignature.getMethod().getAnnotation(Log.class);

(3)根據(jù)自定義注解獲取,注解內(nèi)部的參數(shù)

System.out.println(log.note())

4、高級(jí)操作

場景:不僅需要獲取返回值,還得知道方法參數(shù)的值,而且方法參數(shù)的值不能作為返回值,這個(gè)該怎么辦呢?

秀操作開始:

第一步: 在注解上寫 "#" + "方法參數(shù)的名"

@Log(note = "#note")
public int rdAuditReturn(String note) {
     System.out.println(note)
     xxxx.....
     xxxx.....
     業(yè)務(wù)代碼.....
     xxxx.....
     xxxx....
    return cId;
}

第二步: 實(shí)現(xiàn)切面,只要調(diào)用這個(gè)方法,就可以得到note的值了

private final ExpressionParser parser = new SpelExpressionParser();
private final EvaluationContext evaluationContext = new StandardEvaluationContext();
private void getNote(JoinPoint joinPoint, StringBuilder noteBuilder, String note) throws NoSuchMethodException {
    if (!StringUtils.isBlank(note)) {
        Class<?> targetCls = joinPoint.getTarget().getClass();
        MethodSignature ms = (MethodSignature) joinPoint.getSignature();
        Method targetMethod = targetCls.getDeclaredMethod(ms.getName(), ms.getParameterTypes());
        ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
        String[] parameterNames = pnd.getParameterNames(targetMethod);
        Object[] args = joinPoint.getArgs();
        for (int i = 0; i < args.length; ++i) {
            int index = i;
            Optional.ofNullable(args[i]).ifPresent(param -> {
                String paramName = parameterNames[index];
                this.evaluationContext.setVariable(paramName, param);
            });
        }
        Optional.ofNullable(this.parser.parseExpression(note).getValue(this.evaluationContext)).ifPresent(k ->
                noteBuilder.append((String) k)
        );
    }
}

以上就是SpringBoot 自定義注解異步記錄復(fù)雜日志詳解的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot注解異步日志記錄的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 一文帶你探索Java中的通配符與泛型

    一文帶你探索Java中的通配符與泛型

    Java 語言中的泛型是一種強(qiáng)大的特性,它可以將類型參數(shù)化,使得代碼更具通用性和安全性,本文將深入講解 Java 通配符和泛型,有需要的小伙伴可以了解下
    2023-12-12
  • 利用Java寫一個(gè)學(xué)生管理系統(tǒng)

    利用Java寫一個(gè)學(xué)生管理系統(tǒng)

    今天這篇文章就給給大家分享利用Java寫一個(gè)學(xué)生管理系統(tǒng)吧,先寫一個(gè)簡單的用List來實(shí)現(xiàn)學(xué)生管理系統(tǒng):
    2021-09-09
  • Intellij IDEA如何自定義注釋模板的實(shí)現(xiàn)方法

    Intellij IDEA如何自定義注釋模板的實(shí)現(xiàn)方法

    這篇文章主要介紹了Intellij IDEA如何自定義注釋模板的實(shí)現(xiàn)方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-05-05
  • Mybatis自定義typeHandle過程解析

    Mybatis自定義typeHandle過程解析

    這篇文章主要介紹了Mybatis自定義typeHandle過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-04-04
  • 深入淺析jcmd:JDK14中的調(diào)試神器

    深入淺析jcmd:JDK14中的調(diào)試神器

    這篇文章主要介紹了jcmd:JDK14中的調(diào)試神器,本文給大家提到了jcmd的語法,通過實(shí)例列舉的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-04-04
  • MyBatis-Plus實(shí)現(xiàn)公共字段自動(dòng)填充功能詳解

    MyBatis-Plus實(shí)現(xiàn)公共字段自動(dòng)填充功能詳解

    在開發(fā)中經(jīng)常遇到多個(gè)實(shí)體類有共同的屬性字段,這些字段屬于公共字段,也就是很多表中都有這些字段,能不能對(duì)于這些公共字段在某個(gè)地方統(tǒng)一處理,來簡化開發(fā)呢?MyBatis-Plus就提供了這一功能,本文就來為大家詳細(xì)講講
    2022-08-08
  • Swagger2配置Security授權(quán)認(rèn)證全過程

    Swagger2配置Security授權(quán)認(rèn)證全過程

    這篇文章主要介紹了Swagger2配置Security授權(quán)認(rèn)證全過程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • java ThreadLocal?類常用方法源碼解析

    java ThreadLocal?類常用方法源碼解析

    這篇文章主要為大家介紹了java ThreadLocal類常用方法源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • Java基于Socket實(shí)現(xiàn)網(wǎng)絡(luò)編程實(shí)例詳解

    Java基于Socket實(shí)現(xiàn)網(wǎng)絡(luò)編程實(shí)例詳解

    本文主要給大家介紹的是Java基于Socket實(shí)現(xiàn)網(wǎng)絡(luò)編程的實(shí)例,并給大家介紹了TCP與UDP傳輸協(xié)議,有需要的小伙伴可以來參考下
    2016-07-07
  • Java中String判斷值為null或空及地址是否相等的問題

    Java中String判斷值為null或空及地址是否相等的問題

    這篇文章主要介紹了Java中String判斷值為null或空及地址是否相等的問題,文中舉了簡單的例子對(duì)字符串類型的值和地址問題進(jìn)行講解,需要的朋友可以參考下
    2016-01-01

最新評(píng)論

阳高县| 禹州市| 宁武县| 临武县| 青神县| 靖安县| 四会市| 天祝| 安阳县| 彰武县| 上蔡县| 西青区| 阿克陶县| 嘉峪关市| 吉首市| 黄平县| 扶风县| 凉城县| 扎鲁特旗| 胶南市| 阿城市| 达拉特旗| 永仁县| 会东县| 隆昌县| 虞城县| 萨迦县| 叙永县| 长宁县| 信宜市| 东宁县| 泾川县| 宜宾市| 乌海市| 凤城市| 盐池县| 仁寿县| 阜平县| 安远县| 丹东市| 呈贡县|