首先我们需要了解AOP和反射的原理,我这里主要是实战的开发,所以就只对AOP和反射进行简单的概述。

AOP指的是面向切面进行编程,就是正对某一个平面进行竖向的切割,生活中的例子就好比我们每次吃饭前都要洗手一样,这个洗手的动作就是我们需要在切面进行的方法,而吃饭前就是类似一个切面。

反射指的是利用类加载器加载的类对象反射出该类的属性,方法和注解。比如说我想买个华为手机的电池,可是我又不知道该买怎么样的电池,就可以打电话给华为官网的客服小姐姐,她就会告诉你电池的型号,并且会提供给你具体的购买渠道和地址,这就是一个简单的小例子。

1.我们需要引入支持AOP编程的jar包

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>

2.新建一个LogAspectConfiguration配置类,加上支持面向切面的aspect注解

@Component//spring 组件注解 @Aspect//支持面向切面的注解 @Slf4j//lombor的日志注解 public class LogAspectConfiguration { String controllerName;//保存我们请求的controller类的类名 String method;//保存我们请求的方法名 }

3.建立我们需要切入的切点已经切入的范围

@Component//spring 组件注解 @Aspect//支持面向切面的注解 @Slf4j//lombor的日志注解 public class LogAspectConfiguration { String controllerName;//保存我们请求的controller类的类名 String method;//保存我们请求的方法名 @PointCut("execution(public * com. *.*(..))")//建立切点并标注范围 public void webLog(); }

  1. 开始建立我们的请求建言(俗称通知),我这里指使用了before和afterreturn,因为只需要打印请求参数和返回参数,其实还有after,afterthrows和around三种啦。

@Component//spring 组件注解 @Aspect//支持面向切面的注解 声明是一个切面 @Slf4j//lombor的日志注解 public class LogAspectConfiguration { String controllerName;//保存我们请求的controller类的类名 String method;//保存我们请求的方法名 @PointCut("execution(public * com. *.*(..))")//建立切点并标注范围 public void webLog(); @Before("webLog()")//在请求方法之前访问 public void before(JoinPoint joinPoint) { } @AfterReturn("webLog()")//在请求方法之后访问 public void afterReturn(JoinPoint joinPoint, Object obj) { } }

  1. 开始写我们对请求在方法调用之前进行逻辑处理和日志打印,这里我就不墨迹了,详细的作用我会在用注释进行说明

@Before("logWeb()") public void before(JoinPoint joinPoint){ ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest();//获取httpservletrequest //反射获取目标类的全名 controllerName = joinPoint.getTarget().getClass().getName();//通过目标类反射获取目标类的全名 controllerName = controllerName.substring(controllerName.lastIndexOf(".") 1,controllerName.length());//截取全名 获得当前包的类名 //利用路劲查找处理的方法 method = request.getRequestURL().toString(); if(method.endsWith("html")){ method = method.substring(method.lastIndexOf("/") 1,method.lastIndexOf(".")); }else{ method = method.substring(method.lastIndexOf("/") 1,method.length()); } log.info("准备进入" controllerName "类下" method "方法"); //获取请求的所有参数 Object[] objects = joinPoint.getArgs(); for(Object obj:objects){ //去除参数中的LinkedHashMap if(obj instanceof LinkedHashMap){ continue; } if(obj!=null){ //将对象转成json格式 并进行日志的打印 JSONObject result = JSONObject.fromObject(obj); log.info("请求" controllerName "类下" method "方法的参数为;" result); } } }

这里会有一个疑问,为什么我需要单独去除参数里面的linkedhashmap呢?其实在springMVC中,spring会默认的帮我们在请求目标对象的参数数组中建立一个linkedhashmap,用于存放返回的值,他加入的值大家应该都很熟悉,就是model和map,由于是请求过来的参数,所以我们直接把他过滤掉。如果你们的请求参数中包含list,请再添加一个判断obj instanceof list,想打印请求的路径和一些其他的请求信息,可以通过request自由打印,我这里不需要就没打印了。

  1. 书写放回方法之后的参数打印,这里我们需要注意一点,springMVC的方法放回一般有两种,一种是spring放回的是一个页面,参数通常放在model里面,还有一种是用@responsebody返回的一个对象(当然实际上转化为json格式的string),我们利用放射判断是否获取到@repsonsebody这个注解来判断两中情况

//返回通知 @AfterReturning(value = "logWeb(),returning = "retVal") public void after(JoinPoint joinPoint,Object retVal) { log.info("已经完成" controllerName "类下" method "方法的调用"); //反射获取类加载器加载的目标对象类 Class objectClass = joinPoint.getTarget().getClass(); //反射获取目标对象所有的可见方法 Method[] methods = objectClass.getDeclaredMethods(); Method methodReal = null; //前提你映射的mapper和类名必须保持一致 才可判断类型 for (Method classMethod : methods) { if (classMethod.getName().equals(method)) { methodReal = classMethod; break; } } Object object = methodReal.getAnnotation(ResponseBody.class); //判断是否是responsebody标签注解的类 if(object!=null){ //存在repsonsebody注解 则直接进行将整个放回值进行打印 JSONArray result = JSONArray.fromObject(retVal); }else{ //不存在,则将model里面的所有参数进行打印 Object[] objects = joinPoint.getArgs(); for(Object obj:objects){ if(obj instanceof LinkedHashMap){ Iterator interator = ((LinkedHashMap) obj).keySet().iterator(); while(interator.hasNext()){ Object key = interator.next(); //判断是否为验证对象 if(key.toString().contains("BindingResult")){ continue; } Object value = ((LinkedHashMap) obj).get(key); //如果放回值包含list 则进行list的json转化 if(value instanceof List){ JSONArray arrayResult = JSONArray.fromObject(value); log.info("请求完成后" controllerName "类下" method "方法的返回参数为;" arrayResult); }else{ JSONObject result = JSONObject.fromObject(value); log.info("请求完成后" controllerName "类下" method "方法的返回参数为;" result); } } } } } }

效果图如下:

利用AOP打印接口入参和出参(利用AOP打印接口入参和出参)(1)

,