基于spring boot的controller日志切面处理

基于spring boot的controller日志切面处理

问题需求:项目中需要在日志中记录接口传入值,接口调用ip,接口调用时间,并且入库
处理方式:在controller层加切面处理

1.需要的包,springboot 中aop切面,两个工具类,hutool和jackson
pom如下(版本可以更换):

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-core</artifactId>
            <version>4.5.18</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.9.0</version>
        </dependency>

2.代码编写:

/**
 * <dl>
 * <dt>ControllerAspect</dt>
 * <dd>Description:</dd>
 * <dd>Copyright: Copyright (C) 2019</dd>
 * <dd>Company:</dd>
 * <dd>CreateDate: 2019/7/26</dd>
 * </dl>
 *
 * @author hht
 */
@Component
@Aspect
public class ControllerAspect {

    private Logger logger = LoggerFactory.getLogger(ControllerAspect.class);

    // 定义切点Pointcut
    @Pointcut( "execution(* com.test.test.controller..*(..))")
    public void executeController() {
    }

    @Before("executeController()")
    public void before(JoinPoint joinPoint) throws Exception{
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
        HttpServletRequest request = servletRequestAttributes.getRequest();
        //这一步获取到的方法有可能是代理方法也有可能是真实方法
        Method m = ((MethodSignature) joinPoint.getSignature()).getMethod();
        //判断代理对象本身是否是连接点所在的目标对象,不是的话就要通过反射重新获取真实方法
        if (joinPoint.getThis().getClass() != joinPoint.getTarget().getClass()) {
            m = ReflectUtil.getMethod(joinPoint.getTarget().getClass(), m.getName(), m.getParameterTypes());
        }
        //通过真实方法获取该方法的参数名称
        LocalVariableTableParameterNameDiscoverer paramNames = new LocalVariableTableParameterNameDiscoverer();
        String[] parameterNames = paramNames.getParameterNames(m);
        //获取连接点方法运行时的入参列表
        Object[] args = joinPoint.getArgs();
        //将参数名称与入参值一一对应起来
        Map<String, Object> params = new HashMap<>();
        //自己写的一个判空类方法
        if (!TextUtil.isEmpty(parameterNames)){
            for (int i = 0; i < parameterNames.length; i++) {
            	//这里加一个判断,如果使用requestParam接受参数,加了require=false,这里会存现不存在的现象
            	if (TextUtil.isEmpty(args[i])){
                    continue;
                }
                //通过所在类转换,获取值,包含各种封装类都可以
                ObjectMapper objectMapper = new ObjectMapper();
                objectMapper.convertValue(args[i],args[i].getClass());
                params.put(parameterNames[i],JSON.toJSON(objectMapper.convertValue(args[i],args[i].getClass())));
            }
        }
        logger.info("before start:-------------------------------");
        logger.info("URL : " + request.getRequestURL().toString());
        logger.info("HTTP_METHOD : " + request.getMethod());
        logger.info("IP : " + request.getRemoteAddr());
        logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName()+ "." + joinPoint.getSignature().getName());
        //这里经过处理,就可以获得参数名字与值一一对应
        logger.info("ARGS-JSON : " + params);
        //这个就是纯粹拿到参数,值需要自己匹配
        logger.info("ARGS : "+ Arrays.toString(joinPoint.getArgs()));
        logger.info("before end:-------------------------------");

    }


    @AfterReturning(value = "executeController()",returning = "rtv")
    public void after(JoinPoint joinPoint, Object rtv){
        logger.info("before return+++++++++++++++++++++++++++");
        logger.info("responseBody:"+JSON.toJSONString(rtv,SerializerFeature.WriteMapNullValue));
        logger.info("end return++++++++++++++++++++++++++++++++");
    }



}

以上 只要切点修改就可以直接使用,可以更直观的看日志数据,对了,没加异常处理切面。

利用aop环绕进行切面调用数据入库处理

直接上代码

public class ControllerAspect {

    @Autowired
    AsyncTask asyncTask;

    private Logger logger = LoggerFactory.getLogger(ControllerAspect.class);

    // 定义切点Pointcut
    @Pointcut( "execution(* com.test.web..*.*(..))")
    public void executeController() {
    }

    @Before("executeController()")
    public void before(JoinPoint joinPoint) throws Exception{
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
        HttpServletRequest request = servletRequestAttributes.getRequest();
        logger.info("before start:-------------------------------");
        logger.info("URL : " + request.getRequestURL().toString());
        logger.info("HTTP_METHOD : " + request.getMethod());
        logger.info("IP : " + request.getRemoteAddr());
        logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName()+ "." + joinPoint.getSignature().getName());
        logger.info("ARGS-JSON : " + CommonAop.getAopParam(joinPoint));
        logger.info("ARGS : "+ Arrays.toString(joinPoint.getArgs()));
        logger.info("before end:-------------------------------");
    }
    /**
     * 环绕切面数据入库
    */
    @Around("executeController()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
    	//注意!!!!!这里pjp.proceed()这个方法得到返回值,不能多次调用,多次调用整个接口会运行多次,类似hasnext()和next()
        Object object = pjp.proceed();
        try {
            //环绕通知处理方法
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
            HttpServletRequest request = servletRequestAttributes.getRequest();
            //异步入库,这里调用一个异步类方法,进行数据入库
            //CommonAop.getAopParam(pjp)这个方法,参考上面获取参数ProceedingJoinPoint和JoinPoint是类似的
            asyncTask.doInsertLog(request.getRequestURL().toString(),request.getRemoteAddr(), JSON.toJSONString(CommonAop.getAopParam(pjp),SerializerFeature.WriteMapNullValue),request.getMethod(),JSON.toJSONString(object,SerializerFeature.WriteMapNullValue));
        }catch (Exception e){
            e.printStackTrace();
        }
        return object;
    }


    @AfterReturning(value = "executeController()",returning = "rtv")
    public void after(JoinPoint joinPoint, Object rtv){
        logger.info("before return+++++++++++++++++++++++++++");
        logger.info("responseBody:"+JSON.toJSONString(rtv,SerializerFeature.WriteMapNullValue));
        logger.info("end return++++++++++++++++++++++++++++++++");
    }



}

CommonAop


public class CommonAop {

    public static Map<String,Object> getAopParam(JoinPoint joinPoint){
        //这一步获取到的方法有可能是代理方法也有可能是真实方法
        Method m = ((MethodSignature) joinPoint.getSignature()).getMethod();
        //判断代理对象本身是否是连接点所在的目标对象,不是的话就要通过反射重新获取真实方法
        if (joinPoint.getThis().getClass() != joinPoint.getTarget().getClass()) {
            m = ReflectUtil.getMethod(joinPoint.getTarget().getClass(), m.getName(), m.getParameterTypes());
        }
        //获取连接点方法运行时的入参列表
        Object[] args = joinPoint.getArgs();
        return CommonAop.getParam(m,args);
    }

    public static Map<String,Object> getParam(Method m,Object[] args){
        //通过真实方法获取该方法的参数名称
        LocalVariableTableParameterNameDiscoverer paramNames = new LocalVariableTableParameterNameDiscoverer();
        String[] parameterNames = paramNames.getParameterNames(m);

        //将参数名称与入参值一一对应起来
        Map<String, Object> params = new HashMap<>();
        if (!TextUtil.isEmpty(parameterNames)){
            for (int i = 0; i < parameterNames.length; i++) {
                if (TextUtil.isEmpty(args[i])){
                    continue;
                }
                //通过所在类转换,获取值,包含各种封装类都可以
                ObjectMapper objectMapper = new ObjectMapper();
                objectMapper.convertValue(args[i],args[i].getClass());
                params.put(parameterNames[i], JSON.toJSON(objectMapper.convertValue(args[i],args[i].getClass())));
            }
        }
        return params;
    }


}

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
是的,Spring Boot可以基于AOP实现指定切面的拦截处理。AOP(面向切面编程)是一种编程范式,可以将与业务逻辑无关的横切关注点(如日志记录、安全控制、性能统计等)通过切面的方式进行集中管理。 在Spring Boot中,可以通过在切面类上使用`@Aspect`注解来标识一个类为切面类,通过在切面类的方法上使用不同的注解(如`@Before`、`@After`、`@Around`等)来指定不同类型的切点拦截处理。 例如,我们可以定义一个切面类`LogAspect`,用于记录所有控制层方法的执行时间和返回结果: ```java @Aspect @Component public class LogAspect { private static final Logger logger = LoggerFactory.getLogger(LogAspect.class); @Around("execution(* com.example.myapp.controller..*.*(..))") public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable { long startTime = System.currentTimeMillis(); Object result = joinPoint.proceed(); long endTime = System.currentTimeMillis(); long executionTime = endTime - startTime; logger.info("{} executed in {} ms, returned: {}", joinPoint.getSignature(), executionTime, result); return result; } } ``` 在上述代码中,`@Aspect`注解标识了`LogAspect`类为切面类,`@Around`注解指定了要拦截的切点为`com.example.myapp.controller`包下的所有方法,`logExecutionTime()`方法用于记录方法的执行时间和返回结果,并将日志输出到控制台中。 当控制层的方法被调用时,如果方法的签名匹配到了切点表达式,那么`LogAspect`类中对应的切面方法(如`logExecutionTime()`)就会被执行,从而实现了对方法的拦截处理

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值