160919、使用AOP与注解记录Java日志
有些时候,我想要把每个运行过的方法接收到的参数、返回值和执行时间等信息记录(通过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注解有一个参数,如果该方法
可见性是 * (public、protected或private);
名字是 * (任何名字都可以);
参数是 .. (任何参数都可以);
注解为@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日志的更多相关文章
- spring AOP自定义注解方式实现日志管理
今天继续实现AOP,到这里我个人认为是最灵活,可扩展的方式了,就拿日志管理来说,用Spring AOP 自定义注解形式实现日志管理.废话不多说,直接开始!!! 关于配置我还是的再说一遍. 在appli ...
- Spring AOP 自定义注解实现统一日志管理
一.AOP的基本概念: AOP,面向切面编程,常用于日志,事务,权限等业务处理.AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容(Spring核心之一),是函数式编程 ...
- Spring AOP使用注解记录用户操作日志
最后一个方法:核心的日志记录方法 package com.migu.cm.aspect; import com.alibaba.fastjson.JSON; import com.migu.cm.do ...
- Spring AOP的实现记录操作日志
适用场景: 记录接口方法的执行情况,记录相关状态到日志中. 注解类:LogTag.java package com.lichmama.spring.annotation; import java.la ...
- spring AOP自定义注解 实现日志管理
今天继续实现AOP,到这里我个人认为是最灵活,可扩展的方式了,就拿日志管理来说,用Spring AOP 自定义注解形式实现日志管理.废话不多说,直接开始!!! 关于配置我还是的再说一遍. 在appli ...
- SpringBoot系列(十三)统一日志处理,logback+slf4j AOP+自定义注解,走起!
往期精彩推荐 SpringBoot系列(一)idea新建Springboot项目 SpringBoot系列(二)入门知识 springBoot系列(三)配置文件详解 SpringBoot系列(四)we ...
- ELK的高级篇(测试记录各种日志)
一.elk架构已经完成情况情况下 访问限制: 加个x-pack插件 1)一个脚本收集多个日志,if 判断写入es的索引 [root@k8s6 conf.d]# cat file.conf inpu ...
- 来一手 AOP 注解方式进行日志记录
系统日志对于定位/排查问题的重要性不言而喻,相信许多开发和运维都深有体会. 通过日志追踪代码运行状况,模拟系统执行情况,并迅速定位代码/部署环境问题. 系统日志同样也是数据统计/建模的重要依据,通过分 ...
- Spring aop+自定义注解统一记录用户行为日志
写在前面 本文不涉及过多的Spring aop基本概念以及基本用法介绍,以实际场景使用为主. 场景 我们通常有这样一个需求:打印后台接口请求的具体参数,打印接口请求的最终响应结果,以及记录哪个用户在什 ...
随机推荐
- http://blog.csdn.net/tiantiandjava/article/details/46777051
http://blog.csdn.net/tiantiandjava/article/details/46777051
- Oracle本地网络分表模拟分区裁剪
来自讨论贴 http://www.itpub.net/thread-1877111-1-1.html 准备数据表 2014-07-20 01:38:10>create table tb_1 as ...
- 从printf("\40d\n")看转义字符
1. 八进制 十进制 十六进制 二进制:0 1 2 3 4 5 6 7 \0(或省略0,\) ,\28 按道理是错误的,但是C语言把它解释为 \2,8错误了就不考虑 十进制:0 1 2 3 ...
- calc 的使用
通常情况下,一个元素节点使用固定定位absolute和固定定位fixed,会遇到一个问题,如果设置100% ,此时你在对他设置padding,border,margin,它就会撑满 具体情况如下图:
- JQuery AJAX: 了解jQuery AJAX
jQuery AJAX 一.简介1.AJAX是与服务器交换数据的技术,它在不重载全部页面的情况下,实现了对部分网页的更新.AJAX = 异步 JavaScript 和 XML(Asynchronous ...
- dataguard集群搭建
dataguard集群搭建 1. 创建虚拟机 创建一台虚拟机配置如下: 系统Red Hat Enterprise 6(64位).16vCPU.8G内存.两块VM Network类型网卡.三块硬盘分别为 ...
- 30天,O2O速成攻略【7.25北京站】
活动概况 时间:2015年7月25日13:30-16:30 地点:车库咖啡(北京市海淀西大街48号鑫鼎宾馆二层) 主办:APICloud.领通科技.快易行 网址:www.apicloud.com 费用 ...
- ajax异步提交文件
首先 下载jquery和jquery.form.js http://malsup.com/jquery/form/ <script type="text/javascript&qu ...
- Browserify
NodeJS 把 JavaScript 的使用从浏览器端扩展到了服务器端,使得前端开发人员可以用熟悉的语言编写服务器端代码.这一变化使得 NodeJS 很快就流行起来.在 NodeJS 社区中有非常多 ...
- 实验十三_编写、应用中断例程_2 & 总结
编写并安装int 7ch中断例程,功能为完成loop指令的功能 参数:(cx)= 循环次数,(bx)= 位移 以上中断例程安装成功后,对下面的程序进行单步跟踪,尤其注意观察int.iret指令执行前后 ...