惊呆了,Servlet Filter和Spring MVC Interceptor的实现居然这么简单
前言
创建型:单例模式,工厂模式,建造者模式,原型模式
结构型:桥接模式,代理模式,装饰器模式,适配器模式,门面模式,组合模式,享元模式
行为型:观察者模式,模板模式,策略模式,责任链模式,状态模式,迭代器模式,访问者模式
介绍
在工作中,我们经常要和Servlet Filter,Spring MVC Interceptor打交道,虽然我配置写的很6,但是出了问题还得到处google,于是看了一下源码,用Demo的方式来分析一下这两者是怎么工作的。
Servlet Filter
Filter的使用
可能很多小伙伴没怎么用过Filter,我就简单演示一下
1.在web.xml中配置2个Filter
<filter-mapping>
<filter-name>logFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>imageFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2.实现如下,略去了init方法和destroy方法
@WebFilter(filterName = "logFilter")
public class LogFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("LogFilter execute");
chain.doFilter(request, response);
}
}
@WebFilter(filterName = "imageFilter")
public class ImageFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("ImageFilter execute");
chain.doFilter(request, response);
}
}
3.然后你访问任意一个servlet方法,LogFilter和ImageFilter的doFilter方法都会执行
如果你在一个Filter方法后不加chain.doFilter(request, response)
则后续的Filter和Servlet都不会执行,这是为什么呢?看完我手写的Demo你一下就明白了
可以看到Filter可以在请求到达Servlet之前做处理,如
- 请求编码
- 敏感词过滤等
有兴趣的小伙伴可以看看相关的源码
手写Filter的实现
Servlet接口,任何一个web请求都会调用service方法
public interface Servlet {
public void service();
}
public class MyServlet implements Servlet {
@Override
public void service() {
System.out.println("MyServlet的service方法执行了");
}
}
拦截器接口
public interface Filter {
public void doFilter(FilterChain chain);
}
public class LogFilter implements Filter {
@Override
public void doFilter(FilterChain chain) {
System.out.println("LogFilter执行了");
chain.doFilter();
}
}
public class ImageFilter implements Filter {
@Override
public void doFilter(FilterChain chain) {
System.out.println("ImageFilter执行了");
chain.doFilter();
}
}
拦截器链对象
public interface FilterChain {
public void doFilter();
}
public class ApplicationFilterChain implements FilterChain {
private Filter[] filters = new Filter[10];
private Servlet servlet = null;
// 总共的Filter数目
private int n;
// 当前执行完的filter数目
private int pos;
@Override
public void doFilter() {
if (pos < n) {
Filter curFilter = filters[pos++];
curFilter.doFilter(this);
return;
}
servlet.service();
}
public void addFilter(Filter filter) {
// 这里源码有动态扩容的过程,和ArrayList差不多
// 我就不演示了,直接赋数组大小为10了
filters[n++] = filter;
}
public void setServlet(Servlet servlet) {
this.servlet = servlet;
}
}
测试例子
public class Main {
public static void main(String[] args) {
// 在tomcat源码中,会将一个请求封装为一个ApplicationFilterChain对象
// 然后执行ApplicationFilterChain的doFilter方法
ApplicationFilterChain applicationFilterChain = new ApplicationFilterChain();
applicationFilterChain.addFilter(new LogFilter());
applicationFilterChain.addFilter(new ImageFilter());
applicationFilterChain.setServlet(new MyServlet());
// LogFilter执行了
// ImageFilter执行了
// MyServlet的service方法执行了
applicationFilterChain.doFilter();
}
}
如果任意一个Filter方法的最后不加上chain.doFilter(),则后面的拦截器及Servlet都不会执行了。相信你看完ApplicationFilterChain类的doFilter方法一下就明白了,就是一个简单的递归调用
Spring MVC Interceptor
Interceptor的使用
以前写过一篇拦截器应用的文章,有想了解使用方式的小伙伴可以看一下
今天就来分析一下拦截器是怎么实现的?可以通过以下方式实现拦截器
- 实现HandlerInterceptor接口
- 继承HandlerInterceptorAdapter抽象类,按需重写部分实现即可,(HandlerInterceptorAdapter也实现了HandlerInterceptor接口)
总而言之拦截器必须必须实现了HandlerInterceptor接口
HandlerInterceptor有如下3个方法
boolean preHandler():在controller执行之前调用
void postHandler():controller执行之后,且页面渲染之前调用
void afterCompletion():页面渲染之后调用,一般用于资源清理操作
这个图应该很好的显示了一个请求可以被拦截的地方
- Servlet Filter是对一个请求到达Servlet的过程进行拦截
- 而HandlerInterceptor是当请求到达DispatcherServlet后,在Controller的方法执行前后进行拦截
手写Interceptor的实现
我来手写一个Demo,你一下就能明白了
拦截接口,为了方便我这里就只定义了一个方法
public interface HandlerInterceptor {
boolean preHandle();
}
定义如下2个拦截器
public class CostInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle() {
// 这里可以针对传入的参数做一系列事情,我这里就简单返回true了;
System.out.println("CostInterceptor 执行了");
return true;
}
}
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle() {
System.out.println("LoginInterceptor 执行了");
return true;
}
}
存放拦截器的容器
public class HandlerExecutionChain {
private List<HandlerInterceptor> interceptorList = new ArrayList<>();
public void addInterceptor(HandlerInterceptor interceptor) {
interceptorList.add(interceptor);
}
public boolean applyPreHandle() {
for (int i = 0; i < interceptorList.size(); i++) {
HandlerInterceptor interceptor = interceptorList.get(i);
if (!interceptor.preHandle()) {
return false;
}
}
return true;
}
}
演示DispatcherServlet的调用过程
public class Main {
public static void main(String[] args) {
// Spring MVC会根据请求返回一个HandlerExecutionChain对象
// 然后执行HandlerExecutionChain的applyPreHandle方法,controller中的方法
HandlerExecutionChain chain = new HandlerExecutionChain();
chain.addInterceptor(new CostInterceptor());
chain.addInterceptor(new LoginInterceptor());
// 只有拦截器都返回true,才会调用controller的方法
// CostInterceptor 执行了
// LoginInterceptor 执行了
if (!chain.applyPreHandle()) {
return;
}
result();
}
public static void result() {
System.out.println("这是controller的方法");
}
}
如果任意一个Interceptor返回false,则后续的Interceptor和Controller中的方法都不会执行原因在Demo中显而易见
当想对请求增加新的过滤逻辑时,只需要定义一个拦截器即可,完全符合开闭原则。
不知道你意识到没有Servlet Filter和Spring MVC Interceptor都是用责任链模式实现的
来看看DispatcherServlet是怎么做的?和我们上面写的demo一模一样
我们用servlet写web应用时,一个请求地址写一个Servlet类。
而用了spring mvc后,整个应用程序只有一个Servlet即DispatcherServlet,所有的请求都发送到DispatcherServlet,然后通过方法调用的方式执行controller的方法
DispatcherServlet的doDispatch方法源码如下,省略了一部分逻辑(所有的请求都会执行这个方法)
protected void doDispatch() {
// 执行所有HandlerInterceptor的preHandle方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 执行controller中的方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 执行所有HandlerInterceptor的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
Interceptor可以有如下用处
- 记录接口响应时间
- 判断用户是否登陆
- 权限校验等
可以看到Servlet Filter和Spring MVC Interceptor都能对请求进行拦截,只不过时机不同。并且Servlet Filter是Servlet的规范,而Spring MVC Interceptor只能在Spring MVC中使用
欢迎关注
参考博客
[0]https://mp.weixin.qq.com/s/8AIRvz5HOgjw12PbsjZhCQ
[1]https://www.cnblogs.com/xrq730/p/10633761.html
filter源码分析
[2]https://cloud.tencent.com/developer/article/1129724
[3]https://www.jianshu.com/p/be47c9d89175
惊呆了,Servlet Filter和Spring MVC Interceptor的实现居然这么简单的更多相关文章
- 前端控制器是整个MVC框架中最为核心的一块,它主要用来拦截符合要求的外部请求,并把请求分发到不同的控制器去处理,根据控制器处理后的结果,生成相应的响应发送到客户端。前端控制器既可以使用Filter实现(Struts2采用这种方式),也可以使用Servlet来实现(spring MVC框架)。
本文转自http://www.cnblogs.com/davidwang456/p/4090058.html 感谢作者 前端控制器是整个MVC框架中最为核心的一块,它主要用来拦截符合要求的外部请求,并 ...
- spring mvc(注解)上传文件的简单例子
spring mvc(注解)上传文件的简单例子,这有几个需要注意的地方1.form的enctype=”multipart/form-data” 这个是上传文件必须的2.applicationConte ...
- [Spring MVC] - Interceptor 拦截器
Spring MVC中的Interceptor与Struts2的差不多. 下面是一个简单的Interceptor登陆验证例子: 1.需要在spring的配置文件中加入这段: <!-- 自定义拦截 ...
- Spring mvc interceptor配置拦截器,没有登录跳到登录页
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...
- [转]Spring mvc interceptor配置拦截器,没有登录跳到登录页
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.s ...
- Spring mvc Interceptor 解决Session超时配置流程
最近公司内部框架中对Session超时这一功能未实现,由于采用iframe结构,Session超时后,当点击左侧系统菜单时,会在iframe的右侧再次弹出登陆框. 该问题是由于没有设置拦截器造成. 添 ...
- Spring MVC Interceptor
1 在spring-servlet.xml中进行如下配置 <mvc:interceptors> <mvc:interceptor> <mvc:mapping path=& ...
- Spring MVC中自定义拦截器的简单示例
1. 引言 拦截器(Interceptor)实现对每一个请求处理前后进行相关的业务处理,类似于Servlet的Filter. 我们可以让普通的Bean实现HandlerIntercpetor接口或继承 ...
- Spring MVC:使用SimpleUrlHandlerMapping的一个简单例子
实现一个控制器ShirdrnController,如下所示: package org.shirdrn.spring.mvc; import java.util.Date; import javax.s ...
随机推荐
- 写爬虫爬了3w条职位数据,看看当前招聘形势 | 开源
最近有不少程序员又开始找工作了,为了了解目前技术类各职位的数量.薪资.招聘公司.岗位职责及要求,我爬取了拉勾网北上广深4个城市的招聘数据,共3w条.职位包括:人工智能(AI).大数据.数据分析.后端( ...
- vue基础指令学习
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 面试刷题25:jvm的垃圾收集算法?
垃圾收集是java语言的亮点,大大提高了开发人员的效率. 垃圾收集即GC,当内存不足的时候触发,不同的jvm版本算法和机制都有差别. 我是李福春,我在准备面试,今天的问题是: jvm的垃圾回收算法有哪 ...
- OpenCV-Python 轮廓属性 | 二十三
目标 在这里,我们将学习提取一些常用的物体属性,如坚实度,等效直径,掩模图像,平均强度等.更多的功能可以在Matlab regionprops文档中找到. (注:质心.面积.周长等也属于这一类,但我们 ...
- 一文彻底读懂MySQL事务的四大隔离级别
前言 之前分析一个死锁问题,发现自己对数据库隔离级别理解还不够清楚,所以趁着这几天假期,整理一下MySQL事务的四大隔离级别相关知识,希望对大家有帮助~ 事务 什么是事务? 事务,由一个有限的数据库操 ...
- D - 渣渣仰慕的爱丽丝 HDU - 6249(背包问题变形)
爱丽丝喜欢集邮.她现在在邮局买一些新邮票. 世界上有各种各样的邮票;它们的编号是1到N.但是,邮票不是单独出售的;必须成套购买.有M套不同的邮票可供选择; 第i套包括编号从li到ri的邮票 .同一枚邮 ...
- P1343 地震逃生(最大流板题)
P1343 地震逃生 题目描述 汶川地震发生时,四川**中学正在上课,一看地震发生,老师们立刻带领x名学生逃跑,整个学校可以抽象地看成一个有向图,图中有n个点,m条边.1号点为教室,n号点为安全地带, ...
- C++STL(二)——vector容器
STL--vector容器 vector对象的概念 vector基本操作 vector对象的初始化.赋值 vector查找.替换(已在上一片 string类 博客总结过了,不再总结) vector添加 ...
- 《Three.js 入门指南》3.1.1 - 基本几何形状 -圆环结(TorusKnotGeometry)
3.1 基本几何形状 圆环结(TorusKnotGeometry) 构造函数 THREE.TorusKnotGeometry(radius, tube, radialSegments, tubular ...
- mysql正则匹配中文时存在的问题
可以看到,目前正则匹配字母没问题,c出现1次,2次,3次匹配的结果都是正常的 接下来我们看看匹配中文的效果 可以看到,当匹配连续出现歪时,结果就开始不正常了 然后我去看了下mysql的中文文档中关于正 ...