日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

使用AOP与注解记录Java日志

發(fā)布時(shí)間:2025/3/21 java 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用AOP与注解记录Java日志 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

有些時(shí)候,我想要把每個(gè)運(yùn)行過的方法接收到的參數(shù)、返回值和執(zhí)行時(shí)間等信息記錄(通過slf4j 和 log4j)下來。在AspectJ、jcabi-aspects和Java注解的幫助下我實(shí)現(xiàn)了這個(gè)想法。

123456public class Foo {??@Loggable??public int power(int x, int p) {????return Math.pow(x, p);??}}

在log4j中可以看到以下輸出:

12[INFO] com.example.Foo #power(2, 10): 1024 in 12μs[INFO] com.example.Foo #power(3, 3): 27 in 4μs

看上去很酷對(duì)吧?接下來我們來看看它是如何工作的。

注解

注解是Java 6中采用的一種技術(shù)(譯注:其實(shí)Java 5就有注解了)。它是一種不會(huì)影響程序運(yùn)行的元編程指令,我們可以用它來對(duì)一些指定的元素(方法、類或者變量)進(jìn)行標(biāo)記。換句話說,注解就是代碼中可以看到的標(biāo)記。一些注解只在編譯階段可見——它們不存在于編譯好的.class文件中,另外一些注解在編譯后仍然可見。

例如,@Override是第一種類型(它的保留類型是SOURCE),而JUnit的@Test是第二種類型(保留類型是RUNTIME),@Loggable——我在上面使用過的是第二種注解,包含在jcabi-aspects中,在編譯后會(huì)留在.class文件中。

值得注意的是,上文的power()方法即便被注解并且編譯,也不會(huì)發(fā)送任何內(nèi)容到slf4j。僅僅是一種用來提醒相關(guān)軟件“請(qǐng)記錄我的執(zhí)行過程”的標(biāo)記, 理解這一點(diǎn)很重要。

AOP

AOP(面向切面編程)是一種可以在對(duì)源代碼不作明顯改動(dòng)的情況下向其加入可執(zhí)行塊的技術(shù)。在上面的示例中,我們不想在實(shí)現(xiàn)類中記錄方法的執(zhí)行,而是用其他類去攔截power()方法的每次調(diào)用,測(cè)量執(zhí)行時(shí)間并把這些信息發(fā)送給slf4j。

我想要使攔截類能識(shí)別@Loggable注解并且記錄下power()方法的每次調(diào)用,當(dāng)然它也應(yīng)該能攔截其他方法。

這個(gè)想法與AOP的出發(fā)點(diǎn)很符合——避免在多個(gè)類中重復(fù)實(shí)現(xiàn)一些共同的功能。

日志是Java主要功能的一個(gè)補(bǔ)充。我們不想往代碼中加入繁雜的日志指令,這樣會(huì)導(dǎo)致代碼可讀性降低,所以我們想在別的地方偷偷地記錄日志。

從AOP的角度來看,我們的解決方案是新建一個(gè)指定切入點(diǎn)和環(huán)繞通知的切面以實(shí)現(xiàn)預(yù)期的功能。

AspectJ

接下來,讓我們來看看這些神奇的注解。首先,讓我們來了解jcabi-aspects是如何用AspectJ來實(shí)現(xiàn)注解的。下面是一個(gè)簡(jiǎn)單例子,你可以在MethodLogger.java中找到全部代碼。

12345678910111213141516@Aspectpublic class MethodLogger {??@Around("execution(* *(..)) && @annotation(Loggable)")??public Object around(ProceedingJoinPoint point) {????long start = System.currentTimeMillis();????Object result = point.proceed();????Logger.info(??????"#%s(%s): %s in %[msec]s",??????MethodSignature.class.cast(point.getSignature()).getMethod().getName(),??????point.getArgs(),??????result,??????System.currentTimeMillis() - start????);????return result;??}}

這個(gè)切面(aspect)里有一個(gè)around()通知,切面用@Aspect注解,通知用@Around注解, 上面提到過這些注解僅僅是在.class文件中做了標(biāo)記,這些標(biāo)記在運(yùn)行時(shí)能提供一些信息給那些對(duì)它們感興趣對(duì)象。

@Around注解有一個(gè)參數(shù),如果該方法

  • 可見性是 * (public、protected或private);
  • 名字是 * (任何名字都可以);
  • 參數(shù)是 .. (任何參數(shù)都可以);
  • 注解為@Loggable
  • 那么通知就會(huì)應(yīng)用到該方法。

    當(dāng)注解方法被調(diào)用的時(shí)就會(huì)被攔截,around()通知會(huì)在被攔截方法之前執(zhí)行,其中 @Around 類型的通知需要一個(gè) ProceedingJoinPoint 類的實(shí)例作為參數(shù),之后返回一個(gè)對(duì)象給power()方法。

    為了調(diào)用power()方法,通知需要調(diào)用join point對(duì)象的proceed()方法。

    接下來編譯并把它加入環(huán)境變量,讓我們的主文件Foo.class能夠調(diào)用它。目前為止一切順利,我們還需要最后一步——把通知運(yùn)轉(zhuǎn)起來。

    二進(jìn)制切面織入

    切面織入(aspect waving)就是將切面應(yīng)用到目標(biāo)對(duì)象從而創(chuàng)建一個(gè)新的代理對(duì)象的過程。切面織入將一些代碼插入原代碼,AspectJ就是這么做的。我們給它兩個(gè)二進(jìn)制Java類Foo.class?和?MethodLogger.class; 它返回三個(gè)類——修改過的Foo.class、Foo$AjcClosure1.class和未修改的MethodLogger.class。

    為了理解如何將不同的通知應(yīng)用于對(duì)應(yīng)的哪個(gè)方法,AspectJ織入在.class文件中使用了注解,并使用反射來瀏覽環(huán)境變量中的所有類。它分析@Around注解中的哪個(gè)方法滿足條件。power()就在此時(shí)被發(fā)現(xiàn)了。

    上述操作需要分為兩步。首先,我們把.java文件編譯。然后AspectJ對(duì)編譯后的文件進(jìn)行織入/修改,織入后的Foo類看起來像下面這樣:

    12345678910public class Foo {??private final MethodLogger logger;??@Loggable??public int power(int x, int p) {????return this.logger.around(point);??}??private int power_aroundBody(int x, int p) {????return Math.pow(x, p);??}}

    AspectJ織入把我們?cè)瓉淼墓δ芤频叫路椒╬ower_aroundBody()中,并把所有的power()調(diào)用重定向到切面類MethodLogger。

    下圖是每次調(diào)用power()的過程:

    圖中那一小塊綠色就是原方法power()。

    如你所見,切面織入過程把類和切面等聯(lián)系起來了。如果沒有織入,它們僅僅是一堆編譯好的二進(jìn)制代碼和注解。

    jcabi-aspects

    jcabi-aspects是一個(gè)含有Loggable注解和MethodLogger切面的JAR庫(kù),它還有其它注解和切面。你不必自己實(shí)現(xiàn)切面,只要在環(huán)境變量加入一些依賴并為切面織入配置好jcabi-maven-plugin。可以到Maven Central獲取最新版本。

    1234567891011121314151617181920212223242526272829<project>??<depenencies>????<dependency>??????<dependency>????????<groupId>com.jcabi</groupId>????????<artifactId>jcabi-aspects</artifactId>??????</dependency>??????<dependency>????????<groupId>org.aspectj</groupId>????????<artifactId>aspectjrt</artifactId>??????</dependency>????</dependency>??</depenencies>??<build>????<plugins>??????<plugin>????????<groupId>com.jcabi</groupId>????????<artifactId>jcabi-maven-plugin</artifactId>????????<executions>??????????<execution>????????????<goals>??????????????<goal>ajc</goal>????????????</goals>??????????</execution>????????</executions>??????</plugin>????</plugins>??</build></project>

    由于織入過程比較復(fù)雜,我用Maven插件和ajc goal做了一個(gè)便捷的織入。你也可以直接用AspectJ,不過我還是推薦你使用jcabi-maven-plugin。

    好了,現(xiàn)在你可以用@com.jcabi.aspects.Loggable?注解你的方法執(zhí)行過程將會(huì)通過slf4j記錄下來。

    如果按上述操作沒有得到預(yù)期效果,歡迎到Github issue提出問題。

    原文鏈接:? javacodegeeks ?翻譯:? ImportNew.com? -? 李 廣

    譯文鏈接:?http://www.importnew.com/13367.html

    本文由?ImportNew?-?李 廣?翻譯自?javacodegeeks

    總結(jié)

    以上是生活随笔為你收集整理的使用AOP与注解记录Java日志的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。