补习系列-springboot 实现拦截的五种姿势

简介

AOP(面向切面编程)常用于解决系统中的一些耦合问题,是一种编程的模式
通过将一些通用逻辑抽取为公共模块,由容器来进行调用,以达到模块间隔离的效果。
其还有一个别名,叫面向关注点编程,把系统中的核心业务逻辑称为核心关注点,而一些通用的非核心逻辑划分为横切关注点

AOP常用于...

日志记录
你需要为你的Web应用程序实现访问日志记录,却又不想在所有接口中一个个进行打点。

安全控制
为URL 实现访问权限控制,自动拦截一些非法访问。

事务
某些业务流程需要在一个事务中串行

异常处理
系统发生处理异常,根据不同的异常返回定制的消息体。

在笔者刚开始接触编程之时,AOP还是个新事物,当时曾认为AOP会大行其道。
果不其然,目前流行的Spring 框架中,AOP已经成为其关键的核心能力。

接下来,我们要看看在SpringBoot 框架中,怎么实现常用的一些拦截操作。

先看看下面的一个Controller方法:

示例

@RestController @RequestMapping("/intercept") public class InterceptController { @PostMapping(value = "/body", consumes = { MediaType.TEXT_PLAIN_VALUE, MediaType.APPLICATION_JSON_UTF8_VALUE }) public String body(@RequestBody MsgBody msg) { return msg == null ? "<EMPTY>" : msg.getContent(); } public static class MsgBody { private String content; public String getContent() { return content; } public void setContent(String content) { this.content = content; } }

在上述代码的 body 方法中,会接受一个MsgBody请求消息体,最终简单的输出content字段。
下面,我们将介绍如何为这个方法实现拦截动作。算起来,共有五种姿势。

姿势一、使用 Filter 接口

Filter 接口由 J2EE 定义,在Servlet执行之前由容器进行调用。
而SpringBoot中声明 Filter 又有两种方式:

1. 注册 FilterRegistrationBean

声明一个FilterRegistrationBean 实例,对Filter 做一系列定义,如下:

@Bean public FilterRegistrationBean customerFilter() { FilterRegistrationBean registration = new FilterRegistrationBean(); // 设置过滤器 registration.setFilter(new CustomerFilter()); // 拦截路由规则 registration.addUrlPatterns("/intercept/*"); // 设置初始化参数 registration.addInitParameter("name", "customFilter"); registration.setName("CustomerFilter"); registration.setOrder(1); return registration; }

其中 CustomerFilter 实现了Filter接口,如下:

public class CustomerFilter implements Filter { private static final Logger logger = LoggerFactory.getLogger(CustomerFilter.class); private String name; @Override public void init(FilterConfig filterConfig) throws ServletException { name = filterConfig.getInitParameter("name"); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { logger.info("Filter {} handle before", name); chain.doFilter(request, response); logger.info("Filter {} handle after", name); } } 2. @WebFilter 注解

为Filter的实现类添加 @WebFilter注解,由SpringBoot 框架扫描后注入

@WebFilter的启用需要配合@ServletComponentScan才能生效

@Component @ServletComponentScan @WebFilter(urlPatterns = "/intercept/*", filterName = "annotateFilter") public class AnnotateFilter implements Filter { private static final Logger logger = LoggerFactory.getLogger(AnnotateFilter.class); private final String name = "annotateFilter"; @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { logger.info("Filter {} handle before", name); chain.doFilter(request, response); logger.info("Filter {} handle after", name); } }

使用注解是最简单的,但其缺点是仍然无法支持 order属性(用于控制Filter的排序)。
而通常的@Order注解只能用于定义Bean的加载顺序,却真正无法控制Filter排序。
这是一个已知问题,参考这里

推荐指数
3 颗星,Filter 定义属于J2EE规范,由Servlet容器调度执行。
由于独立于框架之外,无法使用 Spring 框架的便捷特性,
目前一些第三方组件集成时会使用该方式。

姿势二、HanlderInterceptor

HandlerInterceptor 用于拦截 Controller 方法的执行,其声明了几个方法:
|方法 | 说明|
|-----|-----|
|preHandle | Controller方法执行前调用 |
|preHandle | Controller方法后,视图渲染前调用 |
|afterCompletion| 整个方法执行后(包括异常抛出捕获) |

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpgyfj.html