【环绕切面-日志】

/**
 * 环绕切面 接口调用日志
 */
@Aspect
@Component
public class ApiLogInterceptor {
    @Autowired
    private ApiLogService apiLogService;


    private Logger logger = LoggerFactory.getLogger(this.getClass());
    ThreadLocal<Long> startTime = new ThreadLocal<>();

    //定义切点
    //路径参考自己项目的controller路径
    @Pointcut(value = "execution(* com.test.test.test.test.*.*(..))")//切点定义到包下的所有类所有方法
    public void aopWebLog() {
    }

    @Around("aopWebLog()")
    public Object aroundMethod(ProceedingJoinPoint pjp) throws Throwable {
        startTime.set(System.currentTimeMillis());
        //使用ServletRequestAttributes请求上下文获取方法更多
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        //因为HttpServletRequest的inputStream只能被读取一次,所以将其封装成ContentCachingRequestWrapper,body被读取后,会被它缓存
        RequestWrapper requestWrapper = new RequestWrapper(request);

        HttpServletResponse response = attributes.getResponse();

          //定义入参变量
        String inparams = null;
        String className = pjp.getSignature().getDeclaringTypeName();
        String methodName = pjp.getSignature().getName();
        String name = getName(className, methodName);
        Enumeration headerNames = request.getHeaderNames();

        //根据环绕切面获取入参
        Object[] args = pjp.getArgs();
        //区别是那种请求
        /*这里其实不用区分,直接用if里面的方法即可,
        只是else里面拿不到post方法的入参 ,我比较嫌弃if里的格式不好看,所以会做一个区分*/
        if ("POST".equalsIgnoreCase(request.getMethod())){
            Object arg = args[args.length - 1];
            AppDevFullyVo arg1 = (AppDevFullyVo) arg;
            logger.info("切面参数为:{}", arg1.toString());
            logger.info("res是:{}",inparams=arg1.toString());
        }else {
        	//当入参是一个对象时,没法拿到
             inparams = inparams(request);
            logger.info("res是:{}",inparams);
        }
        //调用整个目标函数执行
        Object obj = pjp.proceed();
        //返回值
        Object ret = pjp.proceed(args);
        String outparams  = ((Result) ret).getResult().toString();
        String message = ((Result) ret).getMessage();
        logger.info("返回值为:"+outparams);
        logger.info("结果信息为:"+message);

        ApiLogPo apiLogPo = new ApiLogPo();
        apiLogPo.setCreatetime(new DateTime());
        apiLogPo.setName(name);
        apiLogPo.setDataorigin(null);
        apiLogPo.setRequestheader(JSONObject.toJSONString(headerNames));
        apiLogPo.setInparams(inparams);
        apiLogPo.setIp(WebLogAspect.getClientIp(request));
        apiLogPo.setOutparams(outparams);
        apiLogPo.setResult(message);
        int elapsedtime = (int) (System.currentTimeMillis() - startTime.get());
        apiLogPo.setElapsedtime(elapsedtime);
        apiLogService.insert(apiLogPo);
        return obj;
    }

    private String getName(String className,String methodName) throws ClassNotFoundException {
        //通过反射获取到类
        Class cl1 = Class.forName(className);
        //拼接方法名,用作后面的比较
        StringBuffer stringBuffer = new StringBuffer("/").append(methodName);
        String s = stringBuffer.toString();
        //获取类中所有的方法
        Method[] methods = cl1.getDeclaredMethods();
        //接口中文名称
        String apiOperationValue = null;
        //接口英文名
        String mapping= null;
        for (Method method : methods) {
            //跳过没有注解发方法(不是接口)
            if (method.getDeclaredAnnotations().length == 0) {
                break;
            }
            ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
            //获取get请求
            GetMapping getAnnotation = method.getDeclaredAnnotation(GetMapping.class);
            if (getAnnotation != null) {
                mapping = getAnnotation.value()[0];
                logger.info("GetMapping"+mapping);
                //如果接口名和获取到的接口名相等,并且apiOperation不为空,就代表是我们请求的接口。拿到接口中文名后就不需要继续循环了,直接返回
                if (s.equals(mapping) && ObjectUtil.isNotEmpty(apiOperation)){
                    apiOperationValue = apiOperation.value();
                    logger.info("GetMapping apiOperationValue"+apiOperationValue);
                    break;
                }
                continue;
            }

            //获取post请求
            PostMapping postAnnotation = method.getDeclaredAnnotation(PostMapping.class);
            if (postAnnotation != null) {
                mapping = postAnnotation.value()[0];
                logger.info("PostMapping"+mapping);
                if (s.equals(mapping) && ObjectUtil.isNotEmpty(apiOperation)){
                    apiOperationValue = apiOperation.value();
                    logger.info("PostMapping apiOperationValue"+apiOperationValue);
                    break;
                }
                continue;
            }

            //获取delete请求
            DeleteMapping deleteAnnotation = method.getDeclaredAnnotation(DeleteMapping.class);
            if (deleteAnnotation != null) {
                mapping = deleteAnnotation.value()[0];
                logger.info("DeleteMapping"+mapping);
                if (s.equals(mapping) && ObjectUtil.isNotEmpty(apiOperation)){
                    apiOperationValue = apiOperation.value();
                    logger.info("DeleteMapping apiOperationValue"+apiOperationValue);
                    break;
                }
                continue;
            }

		//因为这里目前只涉及到get、post、delete请求,所以别的请求没有做判断,如有需要,可以自行增加


        }
        return apiOperationValue;
    }


 

    private String inparams(HttpServletRequest request){
        String result = null;
        Map<String, String> res = new HashMap<String, String>();
        Enumeration<?> temp = request.getParameterNames();
        if (null != temp) {
            while (temp.hasMoreElements()) {
                String en = (String) temp.nextElement();
                String value = request.getParameter(en);
                res.put(en, value);
            }
        }
        result = String.valueOf(res);

        return result;
    }

}

当时做的时候还有另一种需求,仅供参考:需要知道当前点击的页面属于系统的哪个模块下的

我们拿到request里面的referer,分割后会拿到页面路由,再去路由表中进行匹配,拿到模块名称

路由表测试数据
路由表数据

因为这里拿路由信息去拼装,每次都会请求数据库,会造成没必要的数据库压力。按照公司的一些奇怪的要求,不让存到redis,所以放到了一个全局变量中。下面代码中匹配节点名称应该用递归去拼接,但是这里没有,有兴趣的可以自己尝试一下

 /**
     * 根据referer(前端页面路由)获取所属模块
     *
     * @param referer
     * @return
     */
    private String getModule(String referer) {
        if (StrUtil.isBlank(referer)) {
            return null;
        }
        //获取一把锁
        Lock lock = new ReentrantLock();
        if (CollectionUtil.isEmpty(menuDtoList)) {
            try {
                lock.lock();

                //拿到所有pid是0的
                List<MenuDto> result = menuApiService.list(DriConstants.DRI_NAMESPACE, null).getResult();
                List<MenuDto> privateMenuList = new ArrayList<>(result);
                logger.info("size:{}", menuDtoList.size());

                for (int i = 0; i < privateMenuList.size(); i++) {
                    List<MenuDto> dri1 = menuApiService.list(DriConstants.DRI_NAMESPACE, privateMenuList.get(i).getId()).getResult();
                    privateMenuList.addAll(dri1);
                }
                menuDtoList = privateMenuList;
                logger.info("if里面的全局变量大小为:" + menuDtoList.size() + "打印时间为:" + new DateTime());

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                //解锁
                System.out.println("释放锁..." + Thread.currentThread().getId());
                lock.unlock();
            }
        }


        String[] split = referer.split(DriConstants.DRI_NAMESPACE);
        StringBuffer sb = new StringBuffer();
        String s = split[(split.length - 1)];
        sb.append("/dri").append(s);
        //过滤掉componenturi为空的 不然会空指针异常
        List<MenuDto> collect1 = menuDtoList.stream()
                .filter(item -> StrUtil.isNotBlank(item.getComponenturi())).collect(Collectors.toList());
        //过滤出符合referer
        List<MenuDto> collect = collect1.stream()
                .filter(item -> item.getComponenturi().equals(sb.toString()))
                .distinct()
                .collect(Collectors.toList());

        //如果referer不在表里,直接将referer返回出去
        if (CollectionUtil.isEmpty(collect)) {
            return sb.toString();
        }
        StringBuffer module = new StringBuffer();
        StringBuffer module1 = new StringBuffer();
        StringBuffer module2 = new StringBuffer();

        //如果referer的pid是0,直接返回模块名称
        for (MenuDto dto : collect) {
            module.append(dto.getName());
            if (0 == dto.getPid()) {
                return module.toString();
            }
        }

        collect.forEach(item -> {
            if (ObjectUtil.isEmpty(module)) {
                module.append(item.getName());
            }
            menuDtoList.stream()
                    .filter(m -> m.getId().equals(item.getPid()))
                    .collect(Collectors.toList())
                    .forEach(p -> {
                        module1.append(p.getName()).append("/").append(module);
                        if (p.getPid() != 0) {
                            menuDtoList.stream().filter(o -> o.getId().equals(p.getPid()))
                                    .collect(Collectors.toList())
                                    .forEach(u -> {
                                        module2.append(u.getName()).append("/").append(module1);
                                    });
                        }
                    });
        });
        logger.info("module为:" + (ObjectUtil.isEmpty(module2) ? String.valueOf(module1) : String.valueOf(module2)));

        return ObjectUtil.isEmpty(module2) ? String.valueOf(module1) : String.valueOf(module2);
    }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值