因项目需要,要求在方法执行前后打印参数及返回值,上网找了一个可用的,利用aop做的工具,稍作修改后,把入参,出参放到一个json里,成果跟大家分享一下。
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import javassist.*;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* Created with IntelliJ IDEA.
* Description:
* User: spc
* Date: 2018-04-18
* Time: 10:55
*/
@Component //声明组件
@Aspect //声明切面
@ComponentScan //组件自动扫描
@EnableAspectJAutoProxy //spring自动切换JDK动态代理和CGLIB
public class LogRecordAspect {
private Logger logger = LoggerFactory.getLogger(LogRecordAspect.class);
/**
* 打印类method的名称以及参数
* @param point 切面
*/
public JSONObject printMethodParams(JoinPoint point,String temp){
if(point == null){
return new JSONObject();
}
/**
* Signature 包含了方法名、申明类型以及地址等信息
*/
String class_name = point.getTarget().getClass().getName();
String method_name = point.getSignature().getName();
//重新定义日志
logger = LoggerFactory.getLogger(point.getTarget().getClass());
logger.info("class_name = {},temp:"+temp,class_name);
logger.info("method_name = {},temp:"+temp,method_name);
JSONObject paramJson = new JSONObject();
paramJson.put("class_name",class_name);
paramJson.put("method_name",method_name);
paramJson.put("temp",temp);
/**
* 获取方法的参数值数组。
*/
Object[] method_args = point.getArgs();
try {
//获取方法参数名称
String[] paramNames = getFieldsName(class_name, method_name);
//打印方法的参数名和参数值
String param_name = logParam(paramNames,method_args);
paramJson.put("param_name",JSONObject.parse(param_name));
} catch (Exception e) {
e.printStackTrace();
}
return paramJson;
}
/**
* 使用javassist来获取方法参数名称
* @param class_name 类名
* @param method_name 方法名
* @return
* @throws Exception
*/
private String[] getFieldsName(String class_name, String method_name) throws Exception {
Class> clazz = Class.forName(class_name);
String clazz_name = clazz.getName();
ClassPool pool = ClassPool.getDefault();
ClassClassPath classPath = new ClassClassPath(clazz);
pool.insertClassPath(classPath);
CtClass ctClass = pool.get(clazz_name);
CtMethod ctMethod = ctClass.getDeclaredMethod(method_name);
MethodInfo methodInfo = ctMethod.getMethodInfo();
CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
if(attr == null){
return null;
}
String[] paramsArgsName = new String[ctMethod.getParameterTypes().length];
// 如果是静态方法,则第一就是参数
// 如果不是静态方法,则第一个是"this",然后才是方法的参数
// 我接口中没有写public修饰词,导致我的数组少一位参数,所以再往后一位,原本应该是 XX ? 0 : 1
int pos = Modifier.isStatic(ctMethod.getModifiers()) ? 1 : 2;
for (int i=0;i
paramsArgsName[i] = attr.variableName(i+pos);
}
return paramsArgsName;
}
/**
* 判断是否为基本类型:包括String
* @param clazz clazz
* @return true:是; false:不是
*/
private boolean isPrimite(Class> clazz){
if (clazz.isPrimitive() || clazz == String.class){
return true;
}else {
return false;
}
}
/**
* 打印方法参数值 基本类型直接打印,非基本类型需要重写toString方法
* @param paramsArgsName 方法参数名数组
* @param paramsArgsValue 方法参数值数组
*/
private String logParam(String[] paramsArgsName,Object[] paramsArgsValue){
StringBuffer buffer = new StringBuffer();
if(ArrayUtils.isEmpty(paramsArgsName) || ArrayUtils.isEmpty(paramsArgsValue)){
buffer.append("该方法没有参数");
return buffer.toString();
}
for (int i=0;i
//参数名
String name = paramsArgsName[i];
//参数值
Object value = paramsArgsValue[i];
buffer.append("\""+name+"\":");
if(isPrimite(value.getClass())){
buffer.append("\""+value+"\",");
}else {
buffer.append(JSON.toJSONString(value)+",");
}
}
return "{"+buffer.toString().substring(0,buffer.toString().length()-1)+"}";
}
/**
*
Before : 在方法执行前进行切面*
execution : 定义切面表达式*
public * com.eparty.ccp.*.impl..*.*(..)
*
public :匹配所有目标类的public方法,不写则匹配所有访问权限*
第一个* :方法返回值类型,*代表所有类型*
第二个* :包路径的通配符*
第三个..* :表示impl这个目录下所有的类,包括子目录的类*
第四个*(..) : *表示所有任意方法名,..表示任意参数*
* @param
*/
/*@Before("execution(* com.abc.service.*.many*(..))")
public void before(JoinPoint point) {
this.printMethodParams(point);
}*/
@Around("execution(* com.abc.service.*.many*(..))")
public Object around(ProceedingJoinPoint pj) throws Throwable {
Long temp = System.currentTimeMillis();
JSONObject paramJson = this.printMethodParams(pj,String.valueOf(temp));
logger.info("请求前:"+paramJson.toString());
Object retVal = pj.proceed();
JSONObject returnJson = new JSONObject();
returnJson.put("temp",temp);
returnJson.put("class_name",paramJson.get("class_name"));
returnJson.put("method_name",paramJson.get("method_name"));
returnJson.put("return_name",retVal);
logger.info("请求后:"+returnJson.toString());
return retVal;
}
}
备注:
1.ProceedingJoinPoint是JoinPoint的子类。
2.around方法应该有返回值,拿了没放回去后果就是丢失返回值....
public Object around(……){
……
return retVal;
}