Sometimes, I want to log (through slf4j and log4j) every execution of a method, seeing what arguments it receives, what it returns and how much time every execution takes. This is how I'm doing it, with help of AspectJ,jcabi-aspects and Java 6 annotations:

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

This is what I see in log4j output:

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

Nice, isn't it? Now, let's see how it works.

Annotation with Runtime Retention

Annotations is a technique introduced in Java 6. It is a meta-programming instrument that doesn't change the way code works, but gives marks to certain elements (methods, classes or variables). In other words, annotations are just markers attached to the code that can be seen and read. Some annotations are designed to be seen at compile time only — they don't exist in.class files after compilation. Others remain visible after compilation and can be accessed in runtime.

For example, @Override is of the first type (its retention type is SOURCE), while @Test from JUnit is of the second type (retention type is RUNTIME). @Loggable — the one I'm using in the script above — is an annotation of the second type, from jcabi-aspects. It stays with the bytecode in the.class file after compilation.

Again, it is important to understand that even though method power() is annotated and compiled, it doesn't send anything to slf4j so far. It just contains a marker saying "please, log my execution".

Aspect Oriented Programming (AOP)

AOP is a useful technique that enables adding executable blocks to the source code without explicitly changing it. In our example, we don't want to log method execution inside the class. Instead, we want some other class to intercept every call to method power(), measure its execution time and send this information to slf4j.

We want that interceptor to understand our @Loggable annotation and log every call to that specific method power(). And, of course, the same interceptor should be used for other methods where we'll place the same annotation in the future.

This case perfectly fits the original intent of AOP — to avoid re-implementation of some common behavior in multiple classes.

Logging is a supplementary feature to our main functionality, and we don't want to pollute our code with multiple logging instructions. Instead, we want logging to happen behind the scenes.

In terms of AOP, our solution can be explained as creating an aspect thatcross-cuts the code at certain join points and applies an around advice that implements the desired functionality.

AspectJ

Let's see what these magic words mean. But, first, let's see how jcabi-aspectsimplements them using AspectJ (it's a simplified example, full code you can find in 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;
}
}

This is an aspect with a single around advice around() inside. The aspect is annotated with @Aspect and advice is annotated with @Around. As discussed above, these annotations are just markers in .class files. They don't do anything except provide some meta-information to those w ho are interested in runtime.

Annotation @Around has one parameter, which — in this case — says that the advice should be applied to a method if:

  1. its visibility modifier is * (publicprotected or private);

  2. its name is name * (any name);

  3. its arguments are .. (any arguments); and

  4. it is annotated with @Loggable

When a call to an annotated method is to be intercepted, method around()executes before executing the actual method. When a call to methodpower() is to be intercepted, method around() receives an instance of class ProceedingJoinPoint and must return an object, which will be used as a result of method power().

In order to call the original method, power(), the advice has to callproceed() of the join point object.

We compile this aspect and make it available in classpath together with our main file Foo.class. So far so good, but we need to take one last step in order to put our aspect into action — we should apply our advice.

Binary Aspect Weaving

Aspect weaving is the name of the advice applying process. Aspect weaver modifies original code by injecting calls to aspects. AspectJ does exactly that. We give it two binary Java classes Foo.class and MethodLogger.class; it gives back three — modified Foo.classFoo$AjcClosure1.class and unmodified MethodLogger.class.

In order to understand which advices should be applied to which methods, AspectJ weaver is using annotations from .class files. Also, it usesreflection to browse all classes on classpath. It analyzes which methods satisfy the conditions from the @Around annotation. Of course, it finds our method power().

So, there are two steps. First, we compile our .java files using javac and get two files. Then, AspectJ weaves/modifies them and creates its own extra class. Our Foo class looks something like this after weaving:

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 weaver moves our original functionality to a new method,power_aroundBody(), and redirects all power() calls to the aspect classMethodLogger.

Instead of one method power() in class Foo now we have four classes working together. From now on, this is what happens behind the scenes on every call to power():

Original functionality of method power() is indicated by the small green lifeline on the diagram.

As you see, the aspect weaving process connects together classes and aspects, transferring calls between them through join points. Without weaving, both classes and aspects are just compiled Java binaries with attached annotations.

jcabi-aspects

jcabi-aspects is a JAR library that contains Loggable annotation andMethodLogger aspect (btw, there are many more aspects and annotations). You don't need to write your own aspect for method logging. Just add a few dependencies to your classpath and configure jcabi-maven-plugin for aspect weaving (get their latest versions in Maven Central):

<project>
<dependencies>
<dependency>
<groupId>com.jcabi</groupId>
<artifactId>jcabi-aspects</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
</dependencies>
<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>

Since this weaving procedure takes a lot of configuration effort, I created a convenient Maven plugin with an ajc goal, which does the entire aspect weaving job. You can use AspectJ directly, but I recommend that you usejcabi-maven-plugin.

That's it. Now you can use @com.jcabi.aspects.Loggable annotation and your methods will be logged through slf4j.

If something doesn't work as explained, don't hesitate to submit a Github issue.

Java Method Logging with AOP and Annotations的更多相关文章

  1. Method Swizzling和AOP(面向切面编程)实践

    Method Swizzling和AOP(面向切面编程)实践 参考: http://www.cocoachina.com/ios/20150120/10959.html 上一篇介绍了 Objectiv ...

  2. java.util.logging.Logger 使用详解

    概述: 第1部分 创建Logger对象 第2部分 日志级别 第3部分 Handler 第4部分 Formatter 第5部分 自定义 第6部分 Logger的层次关系 参考 第1部分 创建Logger ...

  3. java.util.logging.Logger基础教程

    从JDK1.4开始即引入与日志相关的类java.util.logging.Logger,但由于Log4J的存在,一直未能广泛使用.综合网上各类说法,大致认为: (1)Logger:适用于小型系统,当日 ...

  4. 2.java.util.logging.Logger使用详解

    一.java.util.logging.Logger简介 java.util.logging.Logger不是什么新鲜东西了,1.4就有了,可是因为log4j的存在,这个logger一直沉默着, 其实 ...

  5. Java基础-SSM之Spring的POJO(Plain Old Java Object)实现AOP

    Java基础-SSM之Spring的POJO(Plain Old Java Object)实现AOP 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 上次我分享过Spring传统的A ...

  6. Java动态代理-->Spring AOP

    引述要学习Spring框架的技术内幕,必须事先掌握一些基本的Java知识,正所谓“登高必自卑,涉远必自迩”.以下几项Java知识和Spring框架息息相关,不可不学(我将通过一个系列分别介绍这些Jav ...

  7. Java程序日志:java.util.logging.Logger类

    一.Logger 的级别 比log4j的级别详细,全部定义在java.util.logging.Level里面.各级别按降序排列如下:SEVERE(最高值)WARNINGINFOCONFIGFINEF ...

  8. paip. uapi 过滤器的java php python 实现aop filter

    paip. uapi 过滤器的java php python 实现aop filter filter 是面向切面编程AOP.. 作者Attilax  艾龙,  EMAIL:1466519819@qq. ...

  9. java.util.logging.Logger使用详解

    一.创建Logger对象   static Logger getLogger(String name)           为指定子系统查找或创建一个 logger. static Logger ge ...

随机推荐

  1. 使用Unity创建塔防游戏(Part1)

    How to Create a Tower Defense Game in Unity - Part1 原文作者:Barbara Reichart 文章原译:http://www.cnblogs.co ...

  2. Access一些常用的SQL语句

    您可以将 Microsoft Office Access 2013 用作创建.修改数据库以及处理数据的工具,还可将 Office Access 2013 用作服务器数据库管理系统(如 Microsof ...

  3. VR电影这一新概念在中国电影道路上的探索

    在12月的一个下午,Kevin Geiger正在进行关于VR中的故事讲述的一次再普通不过的演讲.地点是北京电影学院里一个围的水泄不通的场馆,他鼓励大家都来参与电影制作,无论是导演.演员还是电影行业的任 ...

  4. extjs底层源码实现继承分析

    先说明一下在js中一般的继承是怎么写的 var farther=function(name){ this.name=name }; farther.prototype={ constructor:fa ...

  5. 前端之Photoshop切片

    什么是切片 ?     (Photoshop中的切片) 切片:将图片切成几部分,一片一片往上传,这样上传的速度比较快.每个切片作为一个独立的文件传输,文件中包含切片自己的设置.颜色调板.链接.翻转效果 ...

  6. Everything(速度快的文件搜索软件) 1.4.1.801b 汉化绿色版

    软件名称: Everything(速度快的文件搜索软件) 软件语言: 简体中文 授权方式: 免费软件 运行环境: Win7 / Vista / Win2003 / WinXP 软件大小: 2.0MB ...

  7. PDF在线阅读 FlexPaper 惰性加载 ;

    关于PDF在线阅读问题,比较普遍的做法是转换成swf文件来浏览:由于项目需要,就用 flexpaper 来实现了下,功能比较简单:但是文件的惰性加载确实让笔者挠头了一把! 下面是笔者的方法: 采用流的 ...

  8. java基础第二天

    学习了关键字,标识符,数据类型.变量和常量,运算符和表达式,开始编写一些简单的输入输出运算的程序了.

  9. PHP基础语法思维导图

  10. mimi

    <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"& ...