SpringBoot拦截器和 Servlet3.0自定义Filter、Listener
官方文档译文
Spring Boot 包括对嵌入式Tomcat,Jetty和Undertow服务器的支持。大多数开发人员使用适当的“Starter”来获取完全配置的实例。默认情况下,嵌入式服务器在 port 8080上侦听 HTTP 请求。
如果选择在CentOS上使用 Tomcat,请注意,默认情况下,临时目录用于存储已编译的 JSP,文件上载等。当 application 正在运行时,
tmpwatch可能会删除此目录,从而导致失败。要避免此行为,您可能希望自定义tmpwatchconfiguration,以便不删除tomcat.*目录或配置server.tomcat.basedir,以便嵌入式 Tomcat 使用不同的位置。
1 Servlets,Filters 和 listeners
使用嵌入式 servlet 容器时,可以使用 Spring beans 或扫描 Servlet 组件,从 Servlet 规范中注册 servlets,过滤器和所有 listeners(如HttpSessionListener)。
将 Servlets,Filters 和 Listeners 注册为 Spring Beans
作为 Spring bean 的任何Servlet,Filter或 servlet *Listener实例都在嵌入式容器中注册。如果要在 configuration 期间从application.properties引用 value,这可能特别方便。
默认情况下,如果 context 仅包含一个 Servlet,则它将映射到/。在多个 servlet beans 的情况下, bean name 用作路径前缀。将 map 过滤为/*。
如果 convention-based mapping 不够灵活,您可以使用ServletRegistrationBean,FilterRegistrationBean和ServletListenerRegistrationBean classes 进行完全控制。
Spring Boot 附带了许多可能定义 Filter beans 的 auto-configurations。以下是过滤器及其各自 order 的一些示例(lower order value 表示更高的优先级):
| Servlet 过滤器 | 订购 |
|---|---|
OrderedCharacterEncodingFilter |
Ordered.HIGHEST_PRECEDENCE |
WebMvcMetricsFilter |
Ordered.HIGHEST_PRECEDENCE + 1 |
ErrorPageFilter |
Ordered.HIGHEST_PRECEDENCE + 1 |
HttpTraceFilter |
Ordered.LOWEST_PRECEDENCE - 10 |
将 Filter beans 无序排列通常是安全的。
如果需要特定的 order,则应避免配置在Ordered.HIGHEST_PRECEDENCE处读取请求正文的 Filter,因为它可能违反 application 的字符编码 configuration。如果 Servlet 过滤器包装请求,则应使用小于或等于OrderedFilter.REQUEST_WRAPPER_FILTER_MAX_ORDER的 order 进行配置。
2 Servlet Context 初始化
嵌入式 servlet 容器不直接执行 Servlet 3.0 javax.servlet.ServletContainerInitializer接口或 Spring 的org.springframework.web.WebApplicationInitializer接口。这是一个故意的设计决定,旨在降低第方库设计为 war 内部 war 的风险可能 break Spring Boot applications。
如果需要在 Spring Boot application 中执行 servlet context 初始化,则应注册实现org.springframework.boot.web.servlet.ServletContextInitializer接口的 bean。单个onStartup方法提供对ServletContext的访问,如果需要,可以很容易地用作现有WebApplicationInitializer的适配器。
扫描 Servlet,过滤器和 listeners
使用嵌入式容器时,可以使用@ServletComponentScan启用使用@WebServlet,@WebFilter和@WebListener注释的 classes 的自动注册。
@ServletComponentScan在独立容器中没有任何效果,而是使用容器的 built-in 发现机制。
3 ServletWebServerApplicationContext
在引擎盖下,Spring Boot 使用不同类型的ApplicationContext来嵌入 servlet 容器支持。 ServletWebServerApplicationContext是一种特殊类型的WebApplicationContext,它通过搜索单个ServletWebServerFactory bean 来引导自己。通常TomcatServletWebServerFactory,JettyServletWebServerFactory或UndertowServletWebServerFactory已经是 auto-configured。
您通常不需要知道这些 implementation classes。大多数 applications 都是 auto-configured,并且代表您创建了适当的
ApplicationContext和ServletWebServerFactory。
4 自定义嵌入式 Servlet 容器
可以使用 Spring Environment properties 配置 Common servlet 容器设置。通常,您将在application.properties文件中定义 properties。
Common 服务器设置包括:
网络设置:监听传入 HTTP 请求的 port(
server.port),绑定到server.address的接口地址,依此类推。Session 设置:session 是持久性的(
server.servlet.session.persistence),session 超时(server.servlet.session.timeout),session 数据的位置(server.servlet.session.store-dir)和 session-cookie configuration(server.servlet.session.cookie.*)。错误 management:错误页面的位置(
server.error.path)等。
Spring Boot 尝试尽可能多地暴露 common 设置,但这并不总是可行。对于这些情况,专用命名空间提供 server-specific 自定义(请参阅server.tomcat和server.undertow)。例如,可以使用嵌入的 servlet 容器的特定 features 配置访问日志。
有关完整列表,请参阅ServerProperties class。
程序化定制
如果需要以编程方式配置嵌入式 servlet 容器,可以注册实现WebServerFactoryCustomizer接口的 Spring bean。 WebServerFactoryCustomizer提供对ConfigurableServletWebServerFactory的访问,其中包括许多自定义 setter 方法。以下 example 以编程方式显示 port:
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.stereotype.Component;
@Component
public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
@Override
public void customize(ConfigurableServletWebServerFactory server) {
server.setPort(9000);
}
}
TomcatServletWebServerFactory,JettyServletWebServerFactory和UndertowServletWebServerFactory是ConfigurableServletWebServerFactory的专用变体,它们分别为 Tomcat,Jetty 和 Undertow 提供了额外的自定义 setter 方法。
直接自定义 ConfigurableServletWebServerFactory
如果前面的自定义技术太有限,您可以自己注册TomcatServletWebServerFactory,JettyServletWebServerFactory或UndertowServletWebServerFactorybean。
@Bean
public ConfigurableServletWebServerFactory webServerFactory() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.setPort(9000);
factory.setSessionTimeout(10, TimeUnit.MINUTES);
factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/notfound.html"));
return factory;
}
为许多 configuration 选项提供了 setter。如果您需要做一些更具异国情调的事情,还会提供一些受保护的方法“挂钩”。有关详细信息,请参阅source code 文档。
5 JSP 限制
当 running 使用嵌入式 servlet 容器的 Spring Boot application(并打包为可执行存档)时,JSP 支持存在一些限制。
使用 Jetty 和 Tomcat,如果使用 war 包装,它应该可以工作。可执行的 war 在使用
java -jar启动时将起作用,并且还可以部署到任何标准容器。使用可执行文件 jar 时不支持 JSP。Undertow 不支持 JSP。
有一个JSP sample,所以你可以看到如何设置。
代码
1 Filter
启动类
@SpringBootApplication
@ServletComponentScan
public class FilterApplication {
public static void main(String[] args) {
SpringApplication.run(FilterApplication.class, args);
}
}过滤器
**
* @author WGR
* @create 2019/11/14 -- 21:25
*/
@WebFilter(urlPatterns = "/api/*", filterName = "loginFilter")
public class LoginFilter implements Filter {
/**
* 容器加载的时候调用
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("init loginFilter");
}
/**
* 请求被拦截的时候进行调用
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("doFilter loginFilter");
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpServletResponse resp = (HttpServletResponse) servletResponse;
String username = req.getParameter("username");
if ("topcheer".equals(username)) {
filterChain.doFilter(servletRequest,servletResponse);
} else {
resp.sendRedirect("/index.html");
return;
}
}
/**
* 容器被销毁的时候被调用
*/
@Override
public void destroy() {
System.out.println("destroy loginFilter");
}
}web层
/**
* @author WGR
* @create 2019/11/14 -- 21:32
*/
@RestController
public class LoginController {
@GetMapping("/api/test_request")
public Object testRequest(String username){
return username;
}
}html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
index static
<h1>topcheer</h1>
</body>
</html>测试




2 Servlet
- servlet
@WebServlet(name = "userServlet",urlPatterns = "/v1/api/test/customs")
public class UserServlet extends HttpServlet{ @Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getWriter().print("custom sevlet");
resp.getWriter().flush();
resp.getWriter().close();
} @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
this.doGet(req, resp);
} }
- 测试方法
@GetMapping("/v1/api/test/customs")
public Object testServlet(){
return "success";
}
- 测试结果

3 Listener
- listener
@WebListener
public class RequestListener implements ServletRequestListener { @Override
public void requestDestroyed(ServletRequestEvent sre) {
// TODO Auto-generated method stub
System.out.println("======requestDestroyed========");
} @Override
public void requestInitialized(ServletRequestEvent sre) {
System.out.println("======requestInitialized========"); } }
- 测试


4 拦截器
@Configuration
public class CustomWebMvcConfigurer implements WebMvcConfigurer { @Override
public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginIntercepter()).addPathPatterns("/api/*/**");
registry.addInterceptor(new TwoIntercepter()).addPathPatterns("/api/*/**"); //.excludePathPatterns("/api2/xxx/**"); //拦截全部 /*/*/** WebMvcConfigurer.super.addInterceptors(registry);
} }
public class LoginIntercepter implements HandlerInterceptor{
/**
* 进入controller方法之前
*/
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("LoginIntercepter------->preHandle");
// String token = request.getParameter("access_token");
//
// response.getWriter().print("fail");
return HandlerInterceptor.super.preHandle(request, response, handler);
}
/**
* 调用完controller之后,视图渲染之前
*/
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("LoginIntercepter------->postHandle");
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
/**
* 整个完成之后,通常用于资源清理
*/
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("LoginIntercepter------->afterCompletion");
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
public class TwoIntercepter implements HandlerInterceptor{
/**
* 进入对应的controller方法之前
*/
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("TwoIntercepter------>preHandle");
return HandlerInterceptor.super.preHandle(request, response, handler);
}
/**
* controller处理之后,返回对应的视图之前
*/
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("TwoIntercepter------>postHandle");
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
/**
* 整个请求结束后调用,视图渲染后,主要用于资源的清理
*/
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("TwoIntercepter------>afterCompletion");
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
测试:

总结:
1、@Configuration
继承WebMvcConfigurationAdapter(SpringBoot2.X之前旧版本)
SpringBoot2.X 新版本配置拦截器 implements WebMvcConfigurer
2、自定义拦截器 HandlerInterceptor
preHandle:调用Controller某个方法之前
postHandle:Controller之后调用,视图渲染之前,如果控制器Controller出现了异常,则不会执行此方法
afterCompletion:不管有没有异常,这个afterCompletion都会被调用,用于资源清理
3、按照注册顺序进行拦截,先注册,先被拦截
拦截器不生效常见问题:
1)是否有加@Configuration
2)拦截路径是否有问题 ** 和 *
3)拦截器最后路径一定要 “/**”, 如果是目录的话则是 /*/
Filter
是基于函数回调 doFilter(),而Interceptor则是基于AOP思想
Filter在只在Servlet前后起作用,而Interceptor够深入到方法前后、异常抛出前后等
依赖于Servlet容器即web应用中,而Interceptor不依赖于Servlet容器所以可以运行在多种环境。
在接口调用的生命周期里,Interceptor可以被多次调用,而Filter只能在容器初始化时调用一次。
Filter和Interceptor的执行顺序
过滤前->拦截前->action执行->拦截后->过滤后
SpringBoot拦截器和 Servlet3.0自定义Filter、Listener的更多相关文章
- 【SpringBoot】SpringBoot拦截器实战和 Servlet3.0自定义Filter、Listener
=================6.SpringBoot拦截器实战和 Servlet3.0自定义Filter.Listener ============ 1.深入SpringBoot2.x过滤器Fi ...
- 小D课堂 - 零基础入门SpringBoot2.X到实战_第6节 SpringBoot拦截器实战和 Servlet3.0自定义Filter、Listener_24、深入SpringBoot过滤器和Servlet配置过滤器
笔记 1.深入SpringBoot2.x过滤器Filter和使用Servlet3.0配置自定义Filter实战(核心知识) 简介:讲解SpringBoot里面Filter讲解和使用Servle ...
- springboot + 拦截器 + 注解 实现自定义权限验证
springboot + 拦截器 + 注解 实现自定义权限验证最近用到一种前端模板技术:jtwig,在权限控制上没有用springSecurity.因此用拦截器和注解结合实现了权限控制. 1.1 定义 ...
- SpringBoot 拦截器妙用,让你一个人开发整个系统的鉴权模块!
我是陈皮,一个在互联网 Coding 的 ITer,微信搜索「陈皮的JavaLib」第一时间阅读最新文章,回复[资料],即可获得我精心整理的技术资料,电子书籍,一线大厂面试资料和优秀简历模板. Han ...
- springboot拦截器总结
Springboot 拦截器总结 拦截器大体分为两类 : handlerInterceptor 和 methodInterceptor 而methodInterceptor 又有XML 配置方法 和A ...
- SpringBoot拦截器中Bean无法注入(转)
问题 这两天遇到SpringBoot拦截器中Bean无法注入问题.下面介绍我的思考过程和解决过程: 1.由于其他bean在service,controller层注入一点问题也没有,开始根本没意识到Be ...
- Springboot拦截器实现IP黑名单
Springboot拦截器实现IP黑名单 一·业务场景和需要实现的功能 以redis作为IP存储地址实现. 业务场景:针对秒杀活动或者常规电商业务场景等,防止恶意脚本不停的刷接口. 实现功能:写一个拦 ...
- SpringBoot 拦截器获取http请求参数
SpringBoot 拦截器获取http请求参数-- 所有骚操作基础 目录 SpringBoot 拦截器获取http请求参数-- 所有骚操作基础 获取http请求参数是一种刚需 定义拦截器获取请求 为 ...
- Java结合SpringBoot拦截器实现简单的登录认证模块
Java结合SpringBoot拦截器实现简单的登录认证模块 之前在做项目时需要实现一个简单的登录认证的功能,就寻思着使用Spring Boot的拦截器来实现,在此记录一下我的整个实现过程,源码见文章 ...
随机推荐
- tensorflow学习之搭建最简单的神经网络
这几天在B站看莫烦的视频,学习一波,给出视频地址:https://www.bilibili.com/video/av16001891/?p=22 先放出代码 #####搭建神经网络测试 def add ...
- 【Qt开发】Qt标准对话框之QMessageBox
好久没有更新博客,主要是公司里面还在验收一些东西,所以没有及时更新.而且也在写一个基于Qt的画图程序,基本上类似于PS的东西,主要用到的是Qt Graphics View Framework.好了,现 ...
- EF Core 初始化数据库的两种方法。
使用DbContextSeed初始化数据库 添加链接字符串 // This method gets called by the runtime. Use this method to add serv ...
- 使用certbot 为nginx或openresty启用https
certbot 使用letsencrypt 生成免费https证书 以下内容网上整理,原文地址 https://segmentfault.com/a/1190000005797776 -------- ...
- [19/06/04-星期二] HTML基础_实体(转义字符)、图片标签(img)、元标签(meta)、语法规范、内联框架(iframe)、超链接
一.实体(转义字符) 在HTML中,一些诸如<.> 就是普通的小于号和大于号不能直接使用,因为浏览可能会把它当成一个标签去解析,所以需要一些特殊字符去表示这些特殊字符, 这些字符我们称他们 ...
- openstack镜像服务(glance)
1.Use the database access client to connect to the database server as the root user: $ mysql -u root ...
- C++中的新型类型转换
1,C 语言中已经有类型之间的强制转换,C++ 做了改善: 2,C 方式的强制类型转换: 1,(Type) (Expression): 2,Type (Expression): 1,这种方式和上述方式 ...
- CDQ分治总结
\(CDQ\)分治小结 标签:知识点总结 阅读体验:https://zybuluo.com/Junlier/note/1326395 身为一个资深菜鸡 \(CDQ\)分治一开始学了一晚上,后来某一天又 ...
- SSM框架中数据库无法连接的问题
首先是SSM框架中所有的配置都是没有问题的,而且项目在其他人的环境上也能正常访问数据库:那么最有可能的就是数据库版本的问题导致数据库连接不上,服务器给我的报错是: 15:37:25.902 [C3P0 ...
- 什么场景下用redis而不用mysql?
redis我们用作缓存,对查询速度要求比较高的应用场景比较适合.对有复杂逻辑关系的存储不适合. mysql是硬盘存储的,在高性能io要求的项目里不能满足需求,而redis所有数据存在内存里,因此要快得 ...