springboot切面@Aspect失效解决

在springboot项目中我们经常用到切面进行日志打印或者参数校验,但是有时候会出现失效的情况。看完这篇你就知道怎么解决了。

一、代码示例

看下面日志打印示例代码:

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.tools.ant.util.DateUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author zyl
 * @date 2021/8/16
 * @discription
 **/
@Order(1)
@Slf4j
@Aspect
@Component
public class WebLogAspect {

    @Value("${server.port}")
    private String port;

    /** 以 controller 包下定义的所有请求为切入点 */
//    @Pointcut(
//            "@annotation(org.springframework.web.bind.annotation.RequestMapping)" +
//                    "||@annotation(org.springframework.web.bind.annotation.GetMapping)" +
//                    "||@annotation(org.springframework.web.bind.annotation.PostMapping)" +
//                    "||@annotation(org.springframework.web.bind.annotation.PutMapping)" +
//                    "||@annotation(org.springframework.web.bind.annotation.DeleteMapping)"
//    )
    @Pointcut("execution(* com.xxx.xxx.controller.*.*(..))")
    public void webLog() {}
    /**
     * 在切点之前织入
     * @param joinPoint
     * @throws Throwable
     */
    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) {
        // 开始打印请求日志
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        // 打印请求相关参数
        log.info("========================================== Start ==========================================");
        // 打印请求 url
        log.info("url地址: {}", request.getRequestURL().toString().split(port)[1]);
        // 打印 Http method
        log.info("method: {}", request.getMethod());
        // 打印调用 controller 的全路径以及执行方法
        log.info("请求方法: {}", joinPoint.getSignature().toShortString());
        // 打印请求的 IP
        log.info("请求时间: {}", DateUtils.format(new Date(), DateUtils.ISO8601_DATETIME_PATTERN));
        // 打印请求入参
        Object[] args = joinPoint.getArgs();
        List<Object> list = ArrayUtils.isEmpty(args) ? new ArrayList<>() : Arrays.asList(args);
        List<Object> logArgs = list
                .stream()
                .filter(arg -> (!(arg instanceof HttpServletRequest) && !(arg instanceof HttpServletResponse)))
                .collect(Collectors.toList());
        //过滤后序列化无异常
        if(logArgs.size() > 0 && logArgs.get(0) instanceof MultipartFile[]){
            log.info("参数: {}", "文件");
        }else {
            log.info("参数: {}", JSON.toJSONString(logArgs));
        }
        log.info("========================================== Start ==========================================");
        log.info("");
    }
    /**
     * 在切点之后织入
     * @throws Throwable
     */
    @After("webLog()")
    public void doAfter() throws Throwable {
        log.info("=========================================== End ===========================================");
    }
    /**
     * 环绕
     * @param proceedingJoinPoint
     * @return
     * @throws Throwable
     */
    @Around("webLog()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = proceedingJoinPoint.proceed();
        // 执行耗时
        log.info("花费时间: {} ms", System.currentTimeMillis() - startTime);
        // 打印出参
        log.info("返回结果: {}", JSON.toJSONString(result));
        log.info("=========================================== End ===========================================");
        return result;
    }
}

二、代码说明

@Order(1)
@Slf4j
@Aspect
@Component
@Pointcut
@Before
@After
@Around

五个注解:第一个为切面执行序号,有多个切面时使用
                  第二个为打印日志
                  第三个为切面
                  第四个为组件
                  第五个为切点
                  第六个为切点之前织入
                  第七个为在切点之后织入
                  第八个为环绕

三、注意点

1.@Pointcut()切点路径填错会导致切面失效
2.@Order(1)排序重复会导致失效
3.被切面的方法只能是以protect和public修饰(重点)
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐