有些时候,我想要把每个运行过的方法接收到的参数、返回值和执行时间等信息记录(通过slf4j 和 log4j)下来。在AspectJ、jcabi-aspects和Java注解的帮助下我实现了这个想法。

public class Foo {

@Loggable

public int power(int x, int p) {

return Math.pow(x, p);

}

}

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

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

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

看上去很酷对吧?接下来我们来看看它是如何工作的。

注解

注解是Java 6中采用的一种技术(译注:其实Java 5就有注解了)。它是一种不会影响程序运行的元编程指令,我们可以用它来对一些指定的元素(方法、类或者变量)进行标记。换句话说,注解就是代码中可以看到的标记。一些注解只在编译阶段可见——它们不存在于编译好的.class文件中,另外一些注解在编译后仍然可见。

例如,@Override是第一种类型(它的保留类型是SOURCE),而JUnit的@Test是第二种类型(保留类型是RUNTIME),@Loggable——我在上面使用过的是第二种注解,包含在jcabi-aspects中,在编译后会留在.class文件中。

值得注意的是,上文的power()方法即便被注解并且编译,也不会发送任何内容到slf4j。仅仅是一种用来提醒相关软件“请记录我的执行过程”的标记, 理解这一点很重要。

AOP

AOP(面向切面编程)是一种可以在对源代码不作明显改动的情况下向其加入可执行块的技术。在上面的示例中,我们不想在实现类中记录方法的执行,而是用其他类去拦截power()方法的每次调用,测量执行时间并把这些信息发送给slf4j。

我想要使拦截类能识别@Loggable注解并且记录下power()方法的每次调用,当然它也应该能拦截其他方法。

这个想法与AOP的出发点很符合——避免在多个类中重复实现一些共同的功能。

日志是Java主要功能的一个补充。我们不想往代码中加入繁杂的日志指令,这样会导致代码可读性降低,所以我们想在别的地方偷偷地记录日志。

从AOP的角度来看,我们的解决方案是新建一个指定切入点和环绕通知的切面以实现预期的功能。

AspectJ

接下来,让我们来看看这些神奇的注解。首先,让我们来了解jcabi-aspects是如何用AspectJ来实现注解的。下面是一个简单例子,你可以在MethodLogger.java中找到全部代码。

@Aspect

public 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;

}

}

这个切面(aspect)里有一个around()通知,切面用@Aspect注解,通知用@Around注解, 上面提到过这些注解仅仅是在.class文件中做了标记,这些标记在运行时能提供一些信息给那些对它们感兴趣对象。

@Around注解有一个参数,如果该方法

  1. 可见性是 * (public、protected或private);

  2. 名字是 * (任何名字都可以);

  3. 参数是 .. (任何参数都可以);

  4. 注解为@Loggable

那么通知就会应用到该方法。

当注解方法被调用的时就会被拦截,around()通知会在被拦截方法之前执行,其中 @Around 类型的通知需要一个 ProceedingJoinPoint 类的实例作为参数,之后返回一个对象给power()方法。

为了调用power()方法,通知需要调用join point对象的proceed()方法。

接下来编译并把它加入环境变量,让我们的主文件Foo.class能够调用它。目前为止一切顺利,我们还需要最后一步——把通知运转起来。

二进制切面织入

切面织入(aspect waving)就是将切面应用到目标对象从而创建一个新的代理对象的过程。切面织入将一些代码插入原代码,AspectJ就是这么做的。我们给它两个二进制Java类Foo.class 和 MethodLogger.class; 它返回三个类——修改过的Foo.class、Foo$AjcClosure1.class和未修改的MethodLogger.class。

为了理解如何将不同的通知应用于对应的哪个方法,AspectJ织入在.class文件中使用了注解,并使用反射来浏览环境变量中的所有类。它分析@Around注解中的哪个方法满足条件。power()就在此时被发现了。

上述操作需要分为两步。首先,我们把.java文件编译。然后AspectJ对编译后的文件进行织入/修改,织入后的Foo类看起来像下面这样:

public 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织入把我们原来的功能移到新方法power_aroundBody()中,并把所有的power()调用重定向到切面类MethodLogger。

下图是每次调用power()的过程:

图中那一小块绿色就是原方法power()。

如你所见,切面织入过程把类和切面等联系起来了。如果没有织入,它们仅仅是一堆编译好的二进制代码和注解。

jcabi-aspects

jcabi-aspects是一个含有Loggable注解和MethodLogger切面的JAR库,它还有其它注解和切面。你不必自己实现切面,只要在环境变量加入一些依赖并为切面织入配置好jcabi-maven-plugin。可以到Maven Central获取最新版本。

<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>

由于织入过程比较复杂,我用Maven插件和ajc goal做了一个便捷的织入。你也可以直接用AspectJ,不过我还是推荐你使用jcabi-maven-plugin。

好了,现在你可以用@com.jcabi.aspects.Loggable 注解你的方法执行过程将会通过slf4j记录下来。

160919、使用AOP与注解记录Java日志的更多相关文章

  1. spring AOP自定义注解方式实现日志管理

    今天继续实现AOP,到这里我个人认为是最灵活,可扩展的方式了,就拿日志管理来说,用Spring AOP 自定义注解形式实现日志管理.废话不多说,直接开始!!! 关于配置我还是的再说一遍. 在appli ...

  2. Spring AOP 自定义注解实现统一日志管理

    一.AOP的基本概念: AOP,面向切面编程,常用于日志,事务,权限等业务处理.AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容(Spring核心之一),是函数式编程 ...

  3. Spring AOP使用注解记录用户操作日志

    最后一个方法:核心的日志记录方法 package com.migu.cm.aspect; import com.alibaba.fastjson.JSON; import com.migu.cm.do ...

  4. Spring AOP的实现记录操作日志

    适用场景: 记录接口方法的执行情况,记录相关状态到日志中. 注解类:LogTag.java package com.lichmama.spring.annotation; import java.la ...

  5. spring AOP自定义注解 实现日志管理

    今天继续实现AOP,到这里我个人认为是最灵活,可扩展的方式了,就拿日志管理来说,用Spring AOP 自定义注解形式实现日志管理.废话不多说,直接开始!!! 关于配置我还是的再说一遍. 在appli ...

  6. SpringBoot系列(十三)统一日志处理,logback+slf4j AOP+自定义注解,走起!

    往期精彩推荐 SpringBoot系列(一)idea新建Springboot项目 SpringBoot系列(二)入门知识 springBoot系列(三)配置文件详解 SpringBoot系列(四)we ...

  7. ELK的高级篇(测试记录各种日志)

    一.elk架构已经完成情况情况下  访问限制: 加个x-pack插件  1)一个脚本收集多个日志,if 判断写入es的索引 [root@k8s6 conf.d]# cat file.conf inpu ...

  8. 来一手 AOP 注解方式进行日志记录

    系统日志对于定位/排查问题的重要性不言而喻,相信许多开发和运维都深有体会. 通过日志追踪代码运行状况,模拟系统执行情况,并迅速定位代码/部署环境问题. 系统日志同样也是数据统计/建模的重要依据,通过分 ...

  9. Spring aop+自定义注解统一记录用户行为日志

    写在前面 本文不涉及过多的Spring aop基本概念以及基本用法介绍,以实际场景使用为主. 场景 我们通常有这样一个需求:打印后台接口请求的具体参数,打印接口请求的最终响应结果,以及记录哪个用户在什 ...

随机推荐

  1. iOS7跳转AppStore地址

    跳转AppStore地址改变: 由 itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews? ...

  2. python 内容查询小助手

    点我,点我,python os.path  模块: 点我,点我,Python logging模块详解 点我,点我,python两个 list 获取交集,并集,差集的方法 点我,点我,python,my ...

  3. linux mknod命令解析

    linux mknod命令解析 http://www.cnblogs.com/cobbliu/archive/2011/07/05/2389014.html mknod:make node  生成设备 ...

  4. Spring中的工厂模式和单例模式

    Spring预备知识(适合中小型项目) 作用:集成和管理其他框架 工厂模式: A  a  = new A( ); 将类所要创建的对象写入工厂,统一进行管理 package com.spring; pu ...

  5. .net泛型理解

    泛型简介: 泛型(Generic Type)是.NET Framework2.0最强大的功能之一.泛型的主要思想是将算法与数据结构完全分离开,使得一次定义的算法能作用于多种数据结构,从而实现高度可重用 ...

  6. meizu mx4 usb调试

    meizu mx4 打开 USB 调试模式 连接手机 连接 usb 调试前,要确定调试模式已打开 在 设置 -> 辅助功能 -> 开发者选项 -> USB 调试 上打开 USB 调试 ...

  7. android关于The connection to adb is down, and a severe error has occured.这个问题的解决办法

    有时在打开模拟器的时候会出现The connection to adb is down, and a severe error has occured.这个问题,这个问题的解决办法有两个: 方法一:找 ...

  8. mySQL 教程 第7章 存储过程和函数

    存储过程和存储函数 MySQL的存储过程(stored procedure)和函数(stored function)统称为stored routines. 1. MySQL存储过程和函数的区别 函数只 ...

  9. navicat premium 导出表结构

    1.右键 点击public,然后选择 数据传输 2.选择数据库对象,目标选择文件,选择文件的路径,然后开始

  10. grunt搭建前端自动化实践

    grunt是什么? grunt是一个前端构建工具, 每种应用开发, 都有一套构建工具, 例如linux c程序开发, 构建工具是make, java程序的构建工具为maven,web前端经过十多年的发 ...