SpringBoot拦截器和 Servlet3.0自定义Filter、Listener
官方文档译文
Spring Boot 包括对嵌入式Tomcat,Jetty和Undertow服务器的支持。大多数开发人员使用适当的“Starter”来获取完全配置的实例。默认情况下,嵌入式服务器在 port 8080
上侦听 HTTP 请求。
如果选择在CentOS上使用 Tomcat,请注意,默认情况下,临时目录用于存储已编译的 JSP,文件上载等。当 application 正在运行时,
tmpwatch
可能会删除此目录,从而导致失败。要避免此行为,您可能希望自定义tmpwatch
configuration,以便不删除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
或UndertowServletWebServerFactory
bean。
@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的拦截器来实现,在此记录一下我的整个实现过程,源码见文章 ...
随机推荐
- 嵌套的frame
自动化的测试中,iframe的嵌套也是很常见的,对于嵌套的iframe,我们处理的方式是先进入到iframe的父节点, 再进入到子节点,然后可以对子节点里面的对象进行处理和操作.如下的html代码效果 ...
- Java第三周课程总结&实验报告一
第三周课程总结 1.关于面向对象的一些具体内容,明白了类与对象以及Java的封装性和构造方法以及对对象匿名的相关知识. 2.this关键字,它是表示类的成员属性(变量),使用this构造方法时必须放在 ...
- Quartz-第四篇 常规quartz的使用
1.目录结构 2.主要文件 1>引入的jar包,quartz-2.2.2解压后lib下所有的jar包 2>quartz.properties org.quartz.threadPool.t ...
- Spark-Core RDD转换算子-双Value型交互
1.union(otherDataSet) 作用:求并集. 对源 RDD 和参数 RDD 求并集后返回一个新的 RDD scala> val rdd1 = sc.parallelize(1 to ...
- Object.create()的使用方法
Object.create()的使用方法:https://blog.csdn.net/wang252949/article/details/79109437
- [LeetCode] 45. 跳跃游戏 II
题目链接 : https://leetcode-cn.com/problems/jump-game-ii/ 题目描述: 给定一个非负整数数组,你最初位于数组的第一个位置. 数组中的每个元素代表你在该位 ...
- 异步Promise及Async/Await可能最完整入门攻略
此文只介绍Async/Await与Promise基础知识与实际用到注意的问题,将通过很多代码实例进行说明,两个实例代码是setDelay和setDelaySecond. tips:本文系原创转自我的博 ...
- [LOJ 6253] Yazid 的新生舞会
link $solution:$ 不知道为什么别人的代码能写的非常短,难道就是写差分的好处? 这种题肯定是算每个众数的贡献,考虑通过暴力众数求出个数. 现在考虑众数 $x$ ,则在序列 $a$ 中将等 ...
- xss过滤与单例模式(对象的实例永远用一个)
kindeditor里面可以加入script代码,使用re可以过滤掉python有个专门的模块可以处理这种情况,beautifulsoup4 调用代码: content = XSSFilter().p ...
- linux用户管理(useradd、userdel、usermod、groupadd、groupdel、chage、passwd、chpasswd)
一.用户账户配置文件介绍 /etc/passwd 用户账户信息文件/etc/shadow 用户账户密码文件/etc/group 用户组信息文件/etc/gshadow 用户组密码所在文件(基本废弃)/ ...