Filter过滤器

@Component
@Slf4j
public class ForwardFilter implements Filter{
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        if(){
            // 直接跳转到下一个过滤器
            filterChain.doFilter(request, response);
        }else{
            // 转发请求
            targetResponse = forwardRequest(request, forwardRequest);
            response.setStatus();
            response.getOutputStream().write(targetResponse.getBody());
        }
    }
}

Interceptor拦截器

@Component
@Slf4j
public class VerifyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 不拦截swagger页面
        if (request.getRequestURI().contains("api-docs")
                || request.getRequestURI().contains("swagger")) {
            return true;
        }

        if(){
            // 校验通过
            log.info("");
            response.setStatus(200);
            return true;
        }else{
            // 校验不通过
            log.error("");
            response.setStatus(401);
            return false;
        }
    }
}

切面

定义一个切面注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExternalLog {

}

用此注解标记一个method

@Override
@ExternalLog
public List<Instance> getAllInstances() {
    // 被@ExternalLog标记的方法称为切入点Join Point
    log.info("in getAllInstances() and will sleep 2s");
    CompletableFuture<List<Instance>> future = CompletableFuture.supplyAsync(() -> {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return instanceRepository.findAll();
    });
    try {
        return future.get(3000, TimeUnit.MILLISECONDS);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    } catch (ExecutionException e) {
        throw new RuntimeException(e);
    } catch (TimeoutException e) {
        throw new RuntimeException(e);
    }
}

定义一个切面

@Aspect
@Component
@Slf4j
public class ExternalLogAspect {
    @Pointcut("@annotation(cc.cocobolo.common.aop.ExternalLog)")
    public void externalLogPointCut() {
        // 一个切点PointCut
        // 这个切点定义了一个注解@ExternalLog,表示切入点是被此注解标记的方法
    }

    @Around("externalLogPointCut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        // 切面Aspect,用于定义在何处以及如何应用额外逻辑
        long startTime = System.currentTimeMillis();
        log.info("before joinPoint.proceed() at {}", startTime);

        Object result = joinPoint.proceed();
        if (result instanceof List) {
            List<String> listResult = (List<String>) result;
            log.info("joinPoint result size: {}", listResult.size());
        } else {
            log.warn("result abnormal");
        }

        long endTime = System.currentTimeMillis();
        log.info("after joinPoint.proceed() at {}", endTime);
        log.info("cost {} milliseconds", endTime - startTime);
        return result;
    }
}

来理一下切点/切入点/切面的关系:

概念 Name 形式 作用 类比
切点 PointCut 通常由切面类中的方法来定义。这些方法通常使用 @Pointcut 注解进行标注,并且方法体为空 唯一目的是定义一个切点表达式 定义在哪里下刀的规则
切入点 Join Point 实际被拦截的程序执行点。切入点通常指的是被切面拦截的方法,但它也可以是其他连接点,比如字段访问或者异常抛出 方法or异常抛出 被切的对象
切面 Aspect doAround方法 定义在切入点处怎么执行额外的逻辑 切了之后做什么