新建一个类Param,其中包含所有在操作日志中,可能会出现的参数。为什么要这么做?因为每个接口需要的参数都有可能完全不一样,与其去维护大量的判断逻辑,还不如贪心一点,直接传入所有的可能参数。当然后期如果有新的参数需要记录,则需要修改代码。
package spring.aop.log.demo.api.util; import lombok.Data; /** * Param * * @author Lunhao Hu * @date 2019-01-30 17:14 **/ @Data public class Param { /** * 所有可能参数 */ private String id; private String workOrderNumber; private String userId; } 修改模板将模板枚举类中的WARNING修改为如下。
WARNING("警告", "因 工单号 [(%workOrderNumber)] /举报 ID [(%id)] 警告玩家 [(%userId)]");其中的参数,就是要在aop拦截阶段获取并且替换掉的参数。
修改controller我们给之前的controller加上上述模板中国呢的参数。部分代码如下。
@Log(type = "WARNING") @GetMapping("test/{id}") public String test( @PathVariable(name = "id") Integer id, @RequestParam(name = "workOrderNumber") String workOrderNumber, @RequestParam(name = "userId") String userId, @RequestParam(name = "name") String name ) { return "Hello" + id; } 通过反射获取请求的参数在此处分两种情况,一种是简单参数类型,另外一种是复杂参数类型,也就是参数中带了请求DTO的情况。
获取简单参数类型给aop类添加几个私有变量。
/** * 请求中的所有参数 */ private Object[] args; /** * 请求中的所有参数名 */ private String[] paramNames; /** * 参数类 */ private Param params;然后将doAfterReturning中的代码改成如下。
try { // 获取请求详情 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); HttpServletResponse response = attributes.getResponse(); // 获取所有请求参数 Signature signature = point.getSignature(); MethodSignature methodSignature = (MethodSignature) signature; this.paramNames = methodSignature.getParameterNames(); this.args = point.getArgs(); // 实例化参数类 this.params = new Param(); // 注解中的类型 String enumKey = log.type(); String logDetail = Type.valueOf(enumKey).getOperation(); // 从请求传入参数中获取数据 this.getRequestParam(); } catch (Exception e) { System.out.println(e.getMessage()); }首先要做的就是拦截打上了自定义注解的请求。我们可以获取到请求的详情,以及请求中的所有的参数名,以及参数。下面我们就来实现上述代码中的getRequestParam方法。
getRequestParam /** * 获取拦截的请求中的参数 * @param point */ private void getRequestParam() { // 获取简单参数类型 this.getSimpleParam(); } getSimpleParam /** * 获取简单参数类型的值 */ private void getSimpleParam() { // 遍历请求中的参数名 for (String reqParam : this.paramNames) { // 判断该参数在参数类中是否存在 if (this.isExist(reqParam)) { this.setRequestParamValueIntoParam(reqParam); } } }上述代码中,遍历请求所传入的参数名,然后我们实现isExist方法, 来判断这个参数在我们的Param类中是否存在,如果存在我们就再调用setRequestParamValueIntoParam方法,将这个参数名所对应的参数值写入到Param类的实例中。
isExistisExist的代码如下。
/** * 判断该参数在参数类中是否存在(是否是需要记录的参数) * @param targetClass * @param name * @param <T> * @return */ private <T> Boolean isExist(String name) { boolean exist = true; try { String key = this.setFirstLetterUpperCase(name); Method targetClassGetMethod = this.params.getClass().getMethod("get" + key); } catch (NoSuchMethodException e) { exist = false; } return exist; }在上面我们也提到过,在编译的时候会加上getter和setter,所以参数名的首字母都会变成大写,所以我们需要自己实现一个setFirstLetterUpperCase方法,来将我们传入的参数名的首字母变成大写。
setFirstLetterUpperCase代码如下。
/** * 将字符串的首字母大写 * * @param str * @return */ private String setFirstLetterUpperCase(String str) { if (str == null) { return null; } return str.substring(0, 1).toUpperCase() + str.substring(1); } setRequestParamValueIntoParam代码如下。
/** * 从参数中获取 * @param paramName * @return */ private void setRequestParamValueIntoParam(String paramName) { int index = ArrayUtil.indexOf(this.paramNames, paramName); if (index != -1) { String value = String.valueOf(this.args[index]); this.setParam(this.params, paramName, value); } }ArrayUtil是hutool中的一个工具函数。用来判断在一个元素在数组中的下标。
setParam