因项目需要,要求在方法执行前后打印参数及返回值,上网找了一个可用的,利用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.length;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<paramsArgsName.length;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)+"}"; } /** * <li>Before : 在方法执行前进行切面</li> * <li>execution : 定义切面表达式</li> * <p>public * com.eparty.ccp.*.impl..*.*(..) * <li>public :匹配所有目标类的public方法,不写则匹配所有访问权限</li> * <li>第一个* :方法返回值类型,*代表所有类型 </li> * <li>第二个* :包路径的通配符</li> * <li>第三个..* :表示impl这个目录下所有的类,包括子目录的类</li> * <li>第四个*(..) : *表示所有任意方法名,..表示任意参数</li> * </p> * @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;
}