美文网首页
SpringBoot Aop 切面编程

SpringBoot Aop 切面编程

作者: 楚长铭 | 来源:发表于2020-04-29 10:09 被阅读0次

依赖

implementation 'org.springframework.boot:spring-boot-starter-aop'

注解方式记录日志

  • 自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogRecord {

    String value() default "";

}
  • aop
@Slf4j
@Aspect
@Component
public class LogAspect {


    @Pointcut(value = "@annotation(com.ladyishenlong.aop.annotation.LogRecord)")
    public void pointCut() {

    }


    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {

        //获得执行的结果,controller里的异常在这步抛出
        Object result = joinPoint.proceed();
        log.info("接口返回值:{}", result);


        HttpServletRequest request = ((ServletRequestAttributes)
                RequestContextHolder.getRequestAttributes()).getRequest();

        String requestUri = request.getRequestURI();
        log.info("查看访问地址:{}", requestUri);


        //获取参数
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        HashMap<String, Object> paramMap = new HashMap<>();
        for (int i = 0; i < joinPoint.getArgs().length; i++) {
            String paramName = methodSignature.getParameterNames()[i];
            Object paramValue = joinPoint.getArgs()[i];
            paramMap.put(paramName, paramValue);
        }
        log.info("查看参数:{}", paramMap.toString());



        //获取注解内信息
        LogRecord logRecord = methodSignature.getMethod().getAnnotation(LogRecord.class);
        log.info("查看日志信息:{}", logRecord.value());


        return result;
    }

    @AfterReturning(pointcut = "pointCut()", returning = "rvt")
    public void doAfterReturning(JoinPoint joinPoint, Object rvt) {
        // 处理日志信息
        log.info("正常完成接口后日志:{}", rvt);
    }



}
  • 主要关注around内的逻辑,在这里能够获取到@LogRecord传入的数据,以及访问controller传入的参数,可以根据这些数据自定义日志格式
  • 注意:由于aop切入了controller中,如果在这里catch了异常的话,ControllerAdvice的ExceptionHandler将无法捕获这些异常
  @LogRecord("异常接口")
    @GetMapping("/ex")
    public Object ex() {
        if (true) throw new RuntimeException("测试异常");
        return "搞事";
    }
  • 最后在controller加入注解即可

注解方式接口限流

  • 这种方式需要用到redis这种中间件,这里就不详细说明了
  • 自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
@Order(Ordered.HIGHEST_PRECEDENCE)
public @interface RequestLimit {

    /**
     * 允许访问的最大次数
     */
    int count() default 1;

    /**
     * 时间段,单位为毫秒,默认值一分钟
     */
    long time() default 60000;


}
  • aop写法
@Slf4j
@Aspect
@Component
public class RequestLimitInterceptor {


    public static final String REQUEST_LIMIT_PRE = "request-limit-";


    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private HttpServletRequest httpServletRequest;



    @Before("@annotation(***.RequestLimit)")
    public void requestLimit(JoinPoint joinPoint) {

        String ip = IpUtil.getRequestIp(httpServletRequest);

        //获取注解
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        RequestLimit requestLimit = methodSignature.getMethod()
                .getAnnotation(RequestLimit.class);

        if (redisTemplate.hasKey(REQUEST_LIMIT_PRE + ip)) {

            //记录非第一次访问
            String countStr = redisTemplate.opsForValue().get(REQUEST_LIMIT_PRE + ip).toString();
            long count = Long.parseLong(countStr);

            if (count > requestLimit.count()) {
                throw new SystemException(CommonError.REQUEST_TOO_MONEY);
            } else {
                redisTemplate.boundValueOps(REQUEST_LIMIT_PRE + ip).increment(1);
            }

        } else {
            //记录第一次访问
            redisTemplate.opsForValue().set(REQUEST_LIMIT_PRE + ip, 1L,
                    requestLimit.time(), TimeUnit.MILLISECONDS);
        }


    }


}

  • 思路很简单,就是根据ip记录访问次数,超出访问次数就直接抛出异常即可

相关文章

网友评论

      本文标题:SpringBoot Aop 切面编程

      本文链接:https://www.haomeiwen.com/subject/lrzwwhtx.html