简单实现SpringBoot 记录API接口日志,基于AOP+自定义注解方式

1.需求:

        由于需要开放系统中的接口,给第三方调用所以我们需要有一个可以记录第三方调用的通用方案。此时AOP就给我们提供了一个很好的解决方案,既不用修改原有的 filter(过滤器)逻辑,又可以通用。

  AOP是什么

        AOP(面像切面编程)将系统中重复使用到的代码抽离出来,执行时使用动态代理的方式,在不改变原有的业务逻辑上,对我们的方法进行增强。是Spring中的一个重要内容。采用代理机制,Spring底层实现AOP 基于JDK的动态代理 (对实现了接口的类进行代理) 和 Cglib的动态代理两种方式实现(对无实现接口的类进行代理)。

2.如何实现

        2.1:定义一个注解@ApiLogTest

        2.2: 创建一个切面 ApiLogAspect

        2.3:定义我们的切点。@Pointcut(value ="XXX")

        2.4:定义前置增强方法 @Before("XXX")

3.具体实现

     创建自定义注解 ApiLotTest  

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
//接口请求日志记录
public @interface ApiLogTest {
}

       创建一个切面 ApiLotAspect提供给容器读取

此处需要注意 :

使用 request.getParameterMap() 获取参数时,POST 请求发送端content Type必须设置为application/x-www-form-urlencoded;否则会接收不到。
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

@Order(1)  //设置 Spring IOC容器中Bean的执行顺序的优先级 数字越小优先级越高
@Aspect    //定义一个切面 提供给容器读取
@Component //表示为Spring Bean
@Slf4j     //日志
public class ApiLogAspect {

    @Autowired
    private SystemParameterService systemParameterService;
    /**
     * 添加一个切入点 ,以ApiLog注解匹配
     */
    @Pointcut(value = "@annotation(com.XXX.annotation.ApiLogTest)")
    public void apiLog(){}

    /**
     * 前置增强,在执行方法前做处理
     * @param joinPoint
     * @throws Throwable
     */
    @Before("apiLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable{
        log.info("");
        log.info("-----------接口请求日志开始-------------");
        //获取请求信息
        // 获取RequestAttributes
        RequestAttributes requestAttributes =RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
        try {
            if (ObjectUtil.isEmpty(request)) {
                // 请求来源IP
                String clientIp = request.getRemoteAddr();
                log.info("请求来源IP:{}", clientIp);
                // 接口请求地址
                String requestURL = request.getRequestURL().toString();
                log.info("接口请求地址:{}", requestURL);
                // 请求方法
                String requestMethod = request.getMethod();
                log.info("请求方法:{}", requestMethod);
                // 接口路径
                String path = request.getServletPath();
                log.info("接口路径:{}", path);
            }

            // 获取请求的参数
            Map<String, String> rtnMap = converMap(request.getParameterMap());
            // 将参数所在的数组转换成json
            String params = JSON.toJSONString(rtnMap);
            log.info("接口请求参数:{}", JSONUtil.toJsonStr(params));

            // 从切面织入点处通过反射机制获取织入点处的方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            // 获取切入点所在的方法
            Method method = signature.getMethod();

            // 获取请求的类名
            String className = joinPoint.getTarget().getClass().getName();

            // 接口方法参数名称
            String[] parameterNames = signature.getParameterNames();
            log.info("接口方法参数名称:{}", JSONUtil.toJsonStr(parameterNames));

            // 获取接口上使用的注解 一般时一些接口的描述 例如Swagger中的一些注解
            ApiOperation apiLog =signature.getMethod().getAnnotation(ApiOperation.class);
            if (apiLog != null) {
                // 接口概述
                String summary = apiLog.value();
                log.info("接口描述:{}", summary);
            }

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            log.info("-----------接口请求日志结束-------------");
            log.info("");
        }
    }

    public Map<String, String> converMap(Map<String, String[]> paramMap) {
        Map<String, String> rtnMap = new HashMap<String, String>();
        for (String key : paramMap.keySet()) {
            rtnMap.put(key, paramMap.get(key)[0]);
        }
        return rtnMap;
    }

}

4.测试:

  在接口上使用我们自定义注解@ApiLogTest

@RestController
@RequestMapping("/admin/wechatMessage")
public class testController{
    @ApiLogTest
    @GetMapping("/LxdsendTextMesg")
    public Result<Object> LxdsendTextMesg(@RequestParam("str") String s){
		
       System.out.println("----------------------测试-----------"+s);

       return ResultUtil.ok();
    }
}

使用postMan发送一个get请求

 效果

  • 9
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值