`
kane_xie
  • 浏览: 145034 次
社区版块
存档分类
最新评论

AspectJ+Javasist记录日志

阅读更多

在项目中碰到这样一个需求,对一个服务类的每一个方法,在方法开始和结束的时候分别记录一条日志,内容包括方法名,参数名+参数值以及方法执行的时间。

 

@Override
public String get(String key) {
//	long start = System.currentTimeMillis();
//	System.out.println("Begin Method = get, Args = [key=" + key + "]");
	String value = props.getProperty(key);
//	System.out.println("End Method = get, Args = [key=" + key + "], Cost=" + (System.currentTimeMillis() - start)+ "ms");
	return value;
}

 以上注释的代码能够满足要求,但是如果类中的方法比较多得时候,会有大量的冗余代码,不利于维护。这个时候可以用AspectJ实现日志代码的嵌入,但是JointPoint只能拿到参数值,无法拿到参数名。因此考虑用Javasist在初始化的时候先把参数名保存到内存中。以下是代码:

 

 

服务实现类(接口略):

 

package test;

import java.util.Properties;

public class ServiceImpl implements Service {
	private Properties props = new Properties();

	@Override
	public String get(String key) {
		return props.getProperty(key);
	}

	@Override
	public void put(String key, String value) {
		props.put(key, value);
	}

	@Override
	public void putAll(Properties props) {
		this.props.putAll(props);
	}
}

 

 

Aspect类:

 

package test;

import java.util.Map;

import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.NotFoundException;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;

import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

import com.google.common.collect.Maps;

@Aspect
public class LogHelper {
	final static private Logger logger = Logger.getLogger(ServiceImpl.class);
	private static final String DELIMITER = " | ";

	private Map<String, String[]> paraMap = Maps.newHashMap();

	public void init() throws NotFoundException {
		reflectGetParamName(ServiceImpl.class);
	}

	@Pointcut("execution(* (test.*+).*(..))")
	public void aspectjMethod() {
	}

	@Around("aspectjMethod()")
	public Object around(ProceedingJoinPoint call) throws Throwable {
		long startTime = System.nanoTime();
		String methodName = call.getSignature().getName();
		String className = call.getTarget().getClass().getSimpleName();
		Object[] args = call.getArgs();
		StringBuilder begin = new StringBuilder("Begin ").append("Method=").append(methodName).append(", Args=[")
				.append(argsToString(className, methodName, args)).append("]");
		logger.info(begin);

		Object result = call.proceed(args);
		long costTime = (System.nanoTime() - startTime) / 1000;// μs
		StringBuilder end = new StringBuilder("End ").append("Method=").append(methodName).append(", Args=[")
				.append(argsToString(className, methodName, args)).append("], Cost=").append(costTime).append("μs");
		logger.info(end);
		return result;
	}

	private String argsToString(String className, String methodName, Object[] args) {
		String[] paraNames = paraMap.get(className + methodName);
		boolean appendParaName = true;
		if (paraNames == null || paraNames.length != args.length) {
			logger.warn("Fail to get parameter(s)' name. ClassName = " + className + ", MethodName = " + methodName
					+ ", Expected ParaNum = " + args.length);
			appendParaName = false;
		}
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < args.length; i++) {
			Object obj = args[i];
			String paraName = paraNames[i];
			if (sb.length() != 0) {
				sb.append(DELIMITER);
			}
			if (appendParaName) {
				sb.append(paraName).append("=");
			}
			sb.append(String.valueOf(obj));
		}
		return sb.toString();
	}

	private void reflectGetParamName(Class<?> clazz) throws NotFoundException {
		ClassPool pool = ClassPool.getDefault();
		pool.insertClassPath(new ClassClassPath(clazz));
		CtClass cc = pool.get(clazz.getName());
		for (CtMethod cm : cc.getDeclaredMethods()) {
			String methodName = cm.getName();
			// 使用javaassist的反射方法获取方法的参数名
			MethodInfo methodInfo = cm.getMethodInfo();
			CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
			LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute
					.getAttribute(LocalVariableAttribute.tag);
			if (attr == null) {
				throw new NotFoundException("CodeAttribute Not Found. ClassName = " + clazz.getName()
						+ ", MethodName = " + methodName);
			}
			String[] paramNames = new String[cm.getParameterTypes().length];
			int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
			for (int i = 0; i < paramNames.length; i++)
				paramNames[i] = attr.variableName(i + pos);
			paraMap.put(clazz.getSimpleName() + methodName, paramNames);
		}
	}
}

 

  1. 首先初始化的时候调用reflectGetParamName,运用反射获取所有方法的参数名,并保存到paraMap中。
  2. 在方法执行的前后分别打印日志并计算执行时间,方法名和参数值从JoinPoint中获取,参数名从paraMap中获取

配置文件applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>  
  
<beans xmlns="http://www.springframework.org/schema/beans"  
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
         xmlns:aop="http://www.springframework.org/schema/aop"  
         xmlns:tx="http://www.springframework.org/schema/tx"  
         xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd  
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd  
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">  
             
    <!-- 启用AspectJ对Annotation的支持 -->         
    <aop:aspectj-autoproxy/>                  
    <bean id="service" class="test.ServiceImpl"/>  
    <bean id="aspcejHandler" class="test.LogHelper" init-method="init"/>  
</beans>  

 

测试代码:

package test;

import java.util.Properties;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {

	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
		Service service = (Service) context.getBean("service");
		service.put("1", "one");
		service.putAll(new Properties());
		System.out.println(service.get("1"));
	}

}

 

测试结果:

 

15/07/16 10:19:38 INFO test.ServiceImpl: Begin Method=put, Args=[key=1 | value=one]
15/07/16 10:19:38 INFO test.ServiceImpl: End Method=put, Args=[key=1 | value=one], Cost=1539μs
15/07/16 10:19:38 INFO test.ServiceImpl: Begin Method=putAll, Args=[props={}]
15/07/16 10:19:38 INFO test.ServiceImpl: End Method=putAll, Args=[props={}], Cost=148μs
15/07/16 10:19:38 INFO test.ServiceImpl: Begin Method=get, Args=[key=1]
15/07/16 10:19:38 INFO test.ServiceImpl: End Method=get, Args=[key=1], Cost=86μs

 

1
0
分享到:
评论

相关推荐

    aspectj+aspectjrt+aspectjweaver+aopalliance.rar

    例如,可以通过定义一个切面来处理所有服务层方法的日志记录,而无需在每个服务方法中显式调用日志代码。这样提高了代码的整洁性和可维护性。 在实际开发中,首先需要将这些库添加到项目的依赖管理中,然后定义切面...

    AspectJ+Spring编程(原创)

    《AspectJ+Spring编程详解》 在现代软件开发中,模块化和面向切面编程(AOP,Aspect-Oriented Programming)已经成为提升代码可维护性和复用性的重要手段。AspectJ和Spring框架的结合,为Java开发者提供了强大的AOP...

    aspectj+aspectjrt+aspectjweaver

    面向切面编程是一种编程范式,它允许程序员将关注点分离,如日志、事务管理等,从主要业务逻辑中解耦出来。在Java世界中,AspectJ是实现这一目标的最流行库之一。 1. **AspectJ**:AspectJ是一个开源项目,提供了一...

    aspectj + aspectjrt + aspectjweaver

    aspectj-1.8.13.jar(1.9好像是用于java9,换了1.8.X),aspectjrt-1.9.0.RC2.jar、aspectjweaver-1.9.0.RC2.jar均为当前最新版本。

    开发者突击 精通AOP整合应用开发AspectWerkz+AspectJ+Spring.zip

    本书以AOP基础理论为主线,首先讲解AOP的产生与发展、为什么要应用AOP、AOP的核心概念,然后详细讲解AspectWerkz、AspectJ、Spring框架的AOP应用开发技术。 随书附赠的光盘内容为本书开发的案例程序包。本书内容循序...

    开发者突击 精通AOP整合应用开发AspectWerkz+AspectJ+Spring.z01

    本书以AOP基础理论为主线,首先讲解AOP的产生与发展、为什么要应用AOP、AOP的核心概念,然后详细讲解AspectWerkz、AspectJ、Spring框架的AOP应用开发技术。 随书附赠的光盘内容为本书开发的案例程序包。本书内容循序...

    Spring Aop之AspectJ注解配置实现日志管理的方法

    通过使用AspectJ注解,可以轻松地实现日志记录、性能监控、安全检查等功能。 知识点1: AspectJ注解 AspectJ是Java平台上最流行的AOP实现之一,提供了丰富的注解来实现切面编程。AspectJ注解可以用来定义切面、切入...

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

    我们将通过一个简单的例子,演示如何在XML和@AspectJ两种模式下实现日志记录的切面。这将包括定义切入点、通知的编写,以及如何在应用程序上下文中注册这些切面。 7. 性能与选择 尽管@AspectJ提供了更丰富的表达...

    AspectJ in Action: Enterprise AOP with Spring Applications

    - **Spring + AspectJ + Hibernate:**实现持久层操作的事务管理和日志记录。 - **Spring + AspectJ + Swing:**实现GUI应用中的性能监控和错误处理。 - **Spring + AspectJ + JDBC:**优化数据访问层的性能并实现...

    Spring AOP + AspectJ annotation example

    // 日志记录代码 System.out.println("Before method: " + joinPoint.getSignature().getName()); } @AfterReturning("execution(* com.example.service.*.*(..))") public void logAfterReturning(JoinPoint ...

    JavaEE AspectJ基于注解的配置

    在实际应用中,我们可以根据需求灵活使用这些注解,实现诸如日志记录、性能监控、事务管理等功能。例如,对于事务管理,我们可以定义一个`@Around`注解的切面,控制事务的开启、提交、回滚。 在Day01_AspectJAnno...

    Android+反射+代理+静态代理+AspectJ

    面向切面编程(AOP)是一种编程范式,与面向对象编程(OOP)相对,它旨在将关注点分离,使得通用功能(如日志记录、事务管理等)可以从核心业务逻辑中解耦。AOP 的核心概念包括切面、切入点、通知和织入。 **切面...

    Aspectj

    8. **实战应用**: 在实际项目中,AspectJ常用于日志记录、事务管理、性能监控、权限控制等场景。通过切面,可以实现代码的整洁性和低耦合。 9. **学习资源**: 博文链接提供了一个很好的起点,其中详细介绍了AspectJ...

    aspectJ

    6. **应用领域**:AspectJ广泛应用于日志记录、事务管理、权限控制、性能监控等系统级服务,使得这些关注点可以从核心业务逻辑中分离出来,提高了代码的可维护性和复用性。 学习AspectJ,你需要理解其基本概念,并...

    aspectj.jar+aspectjrt.jar+aspectjweaver.jar

    这是面向切面所需的三个jar包,导入即可使用aspectj使用的官网最新版本:aspectj-1.8.6.jar。 aspectj-1.8.6.jar来自官网下载 aspectjrt.jar和aspectjweaver.jar下载自网络 皆非本人创作

    AspectJ经典实例详解

    1. **日志记录**:创建一个切面,当方法被调用时,自动记录日志。这可以避免在每个需要记录日志的地方重复插入代码。 ```java @Aspect public class LoggingAspect { @Before("execution(* ...

    Aspect-Oriented Programming with AspectJ

    使用AspectJ,我们可以创建一个单独的方面来处理日志记录,这样就不需要在每个需要日志的地方添加代码。例如: ```java public aspect LoggingAspect { pointcut businessMethods(): execution(* ...

Global site tag (gtag.js) - Google Analytics