`
kavy
  • 浏览: 888576 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Creating a Logging Aspect with Spring AOP and AspectJ

 
阅读更多

http://thoughtforge.net/665/creating-a-logging-aspect-with-spring-aop-and-aspectj/

 

The Logger

There are a few options to choose from when considering a logging framework and these have evolved over the years so it is probably best to abstract the logging framework from the aspect. For this purpose, I have implemented a simple logging interface and log level enum.

package net.thoughtforge.logger;

public interface Logger {

  boolean isLogLevel(LogLevel logLevel, Class clazz);

  void log(LogLevel logLevel, Class clazz, Throwable throwable, String pattern, Object... arguments);
}
package net.thoughtforge.logger;

public enum LogLevel {
  DEBUG,
  ERROR,
  FATAL,
  INFO,
  TRACE,
  WARN
}

In the majority of cases, I end up using the Commons Logging framework and so have included this implementation of the Logger in the source download.

You may decide that this level of abstraction is overkill and that you are willing to commit to using a specific logging framework. When making this decision, keep in mind that you will be specifying the log level throughout your code base.

The Logging Aspect

I am going to use the @Aspect approach to implement the logging aspect which is to say I will use annotations to specify the advice. I want the logging aspect to log the method name, argument values, return value and any exception thrown so I will use the @Before, @AfterThrowing and @AfterReturning annotations.

@Before(value = "@annotation(loggable)", argNames = "joinPoint, loggable")
public void before(JoinPoint joinPoint, Loggable loggable) {

  Class clazz = joinPoint.getTarget().getClass();
  String name = joinPoint.getSignature().getName();

  if (ArrayUtils.isEmpty(joinPoint.getArgs())) {
    logger.log(loggable.value(), clazz, null, BEFORE_STRING, name, constructArgumentsString(clazz, joinPoint.getArgs()));
  } else {
    logger.log(loggable.value(), clazz, null, BEFORE_WITH_PARAMS_STRING, name, constructArgumentsString(clazz, joinPoint.getArgs()));
  }
}

The ‘Before’ advice simply logs (at the appropriate log level) the method name and the toString value of all arguments (if any). ‘Before’ advice executes when a join point is reached.

@AfterThrowing(value = "@annotation(net.thoughtforge.aspect.Loggable)", throwing = "throwable", argNames = "joinPoint, throwable")
public void afterThrowing(JoinPoint joinPoint, Throwable throwable) {

  Class clazz = joinPoint.getTarget().getClass();
  String name = joinPoint.getSignature().getName();
  logger.log(LogLevel.ERROR, clazz, throwable, AFTER_THROWING, name, throwable.getMessage(), constructArgumentsString(clazz, joinPoint.getArgs()));
}

The ‘AfterThrowing’ advice logs the method name, exception message and the toString value of all arguments (if any). ‘AfterThrowing’ advice executes after a method exits by throwing an exception.

@AfterReturning(value = "@annotation(trace)", returning = "returnValue", argNames = "joinPoint, trace, returnValue")
public void afterReturning(JoinPoint joinPoint, Loggable loggable, Object returnValue) {

  Class clazz = joinPoint.getTarget().getClass();
  String name = joinPoint.getSignature().getName();

  if (joinPoint.getSignature() instanceof MethodSignature) {
    MethodSignature signature = (MethodSignature) joinPoint.getSignature();
    Class returnType = signature.getReturnType();
    if (returnType.getName().compareTo("void") == 0) {
      logger.log(loggable.value(), clazz, null, AFTER_RETURNING_VOID, name, constructArgumentsString(clazz, returnValue));

      return;
    }
  }

  logger.log(loggable.value(), clazz, null, AFTER_RETURNING, name, constructArgumentsString(clazz, returnValue));
}

The ‘AfterReturning’ advice logs the method name and the toString value of the returned value (if any). ‘AfterReturning’ advice executes after a method exits normally.

You will notice that I log the toString value to identify objects. I routinely use the Apache Commons ToStringBuilder to create the toString value. I find this particularly useful when working with persistent entities as it allows me to clearly identify the entity.

Another possible implementation that avoids using the toString method is to use the Apache Commons ReflectionToStringBuilder within the logging aspect to create a string representation of the object being logged.

If you are writing your own toString implementations (the Commons implementation is perfectly adequate) and are implementing an object with complex properties be aware of recursive invocations that may result in a stack overflow exception.

Specifying the Pointcut

A pointcut expression is an expression that specifies where in the code the advice will be applied. With AspectJ, you can create a pointcut by specifying package, class and method attributes among other things. I find the easiest way to specify a pointcut for the logging aspect is by matching methods that have a specific annotation.

package net.thoughtforge.aspect;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import net.thoughtforge.logger.LogLevel;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Loggable {

  LogLevel value();
}

As you can see, the Loggable annotation has one property that specifies the log level at which the log statement should be output. Using the annotation means that developers never need to alter the pointcut expression to add or remove methods to the pointcut. A developer only has to add the annotation to a method to have the logging aspect applied.

package net.thoughtforge.bean;

import java.util.Date;

import net.thoughtforge.aspect.Loggable;
import net.thoughtforge.logger.LogLevel;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.springframework.stereotype.Component;

@Component(value = "simpleBean")
public class SimpleBean {

  private Date dateProperty;

  private Integer integerProperty;

  private String stringProperty;

  @Loggable(value = LogLevel.TRACE)
  public Date getDateProperty() {
    return dateProperty;
  }

  @Loggable(value = LogLevel.TRACE)
  public void setDateProperty(final Date dateProperty) {
    this.dateProperty = dateProperty;
  }

  @Loggable(value = LogLevel.TRACE)
  public Integer getIntegerProperty() {
    return integerProperty;
  }

  @Loggable(value = LogLevel.TRACE)
  public void setIntegerProperty(final Integer integerProperty) {
    this.integerProperty = integerProperty;
  }

  @Loggable(value = LogLevel.TRACE)
  public String getStringProperty() {
    return stringProperty;
  }

  @Loggable(value = LogLevel.TRACE)
  public void setStringProperty(final String stringProperty) {
    this.stringProperty = stringProperty;
  }

  @Override
  public String toString() {
    return new ToStringBuilder(this).append("dateProperty", dateProperty)
        .append("integerProperty", integerProperty).append("stringProperty", stringProperty).toString();
  }
}

The SimpleBean and SimpleBeanSubclass are for demonstration purposes. You can see that each method is annotated with the @Loggable annotation and the log level is set to TRACE. You can obviously use different log levels for different methods as required.

package net.thoughtforge.bean;

import java.math.BigDecimal;

import net.thoughtforge.aspect.Loggable;
import net.thoughtforge.logger.LogLevel;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.springframework.stereotype.Component;

@Component(value = "simpleBeanSubclass")
public class SimpleBeanSubclass extends SimpleBean {

  private BigDecimal decimalProperty;

  @Loggable(value = LogLevel.TRACE)
  public BigDecimal getDecimalProperty() {
    return decimalProperty;
  }

  @Loggable(value = LogLevel.TRACE)
  public void setDecimalProperty(final BigDecimal decimalProperty) {
    this.decimalProperty = decimalProperty;
  }

  @Override
  public String toString() {
    return new ToStringBuilder(this).append("decimalProperty", decimalProperty).appendSuper(super.toString()).toString();
  }
}

Also note the use of the ToStringBuilder to create the toString value. You may choose to use the ReflectionToStringBuilder or some other mechanism.

 

 

分享到:
评论

相关推荐

    spring-aop-jar

    总结起来,"spring-aop-jar"涉及了Spring框架中的面向切面编程模块,包括Spring AOP和AspectJ的集成。通过理解和熟练使用这些组件,开发者可以有效地解耦关注点,提高代码的可维护性和可扩展性。在实际项目中,结合...

    AspectJ in Action: Enterprise AOP with Spring Applications

    ### AspectJ in Action: Enterprise AOP with Spring Applications #### 关键知识点概述 1. **Spring-AspectJ集成:**本书重点介绍了Spring框架与AspectJ相结合的技术优势及其在企业级应用中的强大功能。 2. **...

    Spring AOP依赖jar包

    本篇文章将详细介绍 Spring AOP 的核心概念、如何配置以及所依赖的 Jar 包,特别是 `AspectJ 1.6.12` 版本。 1. **AOP 概念** - **切面(Aspect)**:切面是关注点的模块化,如日志、事务管理等,它们横切多个对象...

    Spring AOP需要的jar

    这些接口为创建和使用切面提供了通用的API,无论你选择使用Spring自己的AOP还是其他AOP实现,如AspectJ,都可以通过aopalliance来实现无缝集成。 另一方面,aspectjweaver-1.8.9.jar是AspectJ库的一部分,用于实现...

    Spring AOP 的实现例子(基于XML配置实现)

    `<aop:config>`是Spring的原生AOP配置,而`<aop:aspectj-autoproxy>`则允许我们使用AspectJ的注解进行AOP配置。 接下来,定义切面(Aspect)。在Spring AOP中,切面是包含一组通知(Advice)的类,这些通知会在特定...

    spring aop 经典例子(原创)

    Spring AOP,全称Aspect-Oriented Programming,是Spring框架中的一个重要组成部分,它引入了面向切面编程的概念,使得开发者可以将关注点分离,更好地实现业务逻辑与系统服务的解耦。在这个经典例子中,我们将深入...

    Spring Aop使用实例

    Spring AOP(Aspect Oriented Programming,面向切面编程)是Spring框架的一个重要组成部分,它提供了一种在不修改原有代码的情况下,通过代理方式添加额外功能的技术。这种技术使得我们可以实现如日志记录、事务...

    Spring Aop的简单实现

    Spring AOP,全称Aspect-Oriented Programming(面向切面编程),是Spring框架的重要组成部分,它为应用程序提供了声明式的企业级服务,如日志、事务管理等。在本项目中,我们将探讨如何通过配置文件实现Spring AOP...

    spring aop的jar包

    AspectJ是Java平台上的一个强大的AOP框架,Spring AOP可以与AspectJ结合使用,实现更复杂的切面逻辑。 2. **org.springframework.context-3.0.5.RELEASE.jar**:这部分包含了Spring上下文模块,提供了对应用环境的...

    aspectJ与XML配置的AOP

    **AspectJ与Spring AOP简介** Aspect Oriented Programming(面向切面编程,简称AOP)是一种编程范式,旨在减少代码中的重复性,提高模块化程度。AspectJ是Java平台上的一个开源AOP框架,它提供了强大的静态和动态...

    springaop.zip

    Spring AOP(面向切面编程)是Spring框架的重要组成部分,它提供了一种强大的方式来实现横切关注点,如日志、事务管理、安全性等,从而使得代码更加模块化和可维护。在本示例中,"springaop.zip" 包含了一个使用XML...

    springAOP中文文档

    1. **启用 AspectJ 注解支持**:为了在 Spring 应用中使用 AspectJ 注解,需要确保项目 classpath 下包含了必要的 AspectJ 类库,包括 `aopalliance.jar`、`aspectjweaver.jar` 等。 2. **配置 AspectJ 切面**:...

    spring aop

    Spring AOP,全称为Aspect-Oriented Programming(面向切面编程),是Spring框架的重要组成部分,它为Java应用程序提供了声明式的企业级服务,如事务管理、日志记录等。AOP的核心概念是切面(Aspect)和通知(Advice...

    Spring的AOP实例(XML+@AspectJ双版本解析+源码+类库)

    **Spring的AOP实例——XML与@AspectJ双版本解析** Spring AOP(面向切面编程)是Spring框架的重要组成部分,它提供了一种模块化和声明式的方式来处理系统的交叉关注点,如日志、事务管理等。AOP的核心概念包括切面...

    spring aop管理xml版

    在Spring框架中,面向切面编程(Aspect Oriented Programming,简称AOP)是一种重要的设计模式,它扩展了传统的面向对象编程(OOP),使得我们可以将关注点分离,特别是那些横切关注点,如日志、事务管理、权限检查...

    SpringAOP依赖包

    Spring AOP,全称为Aspect-Oriented Programming(面向切面编程),是Spring框架的重要组成部分,主要用来处理系统中的横切关注点,如日志、事务管理等。在Spring框架中,AOP通过代理模式实现,提供了声明式企业级...

    SpringMVC3.0-Jar全量包含cglib几个包-aop的aspectj几个包

    总结起来,这个压缩包包含了构建基于SpringMVC 3.0的应用所需要的关键组件,包括Spring MVC本身、Spring框架的核心库,以及用于AOP的cglib、asm、AspectJ和相关依赖。这些库共同工作,提供了一个强大且灵活的平台,...

    aop:aspect

    标题 "aop:aspect" 指涉的是Spring框架中的面向切面编程(Aspect-Oriented Programming, AOP)的一个核心概念。在Spring AOP中,`aop:aspect`是XML配置中定义一个切面的元素,它允许我们将关注点(如日志、事务管理...

    springAOP demo 带错误解决文档

    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.aop.config.internalAutoProxyCreator': Instantiation of bean failed; nested exception is org....

Global site tag (gtag.js) - Google Analytics