1.前言

SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧

本文将通过源码(基于Spring4.3.7)分析,弄清楚SpringMVC过滤器是如何执行的,并自定义过滤器,分清楚过滤器和拦截器的区别

2.源码分析

web.xml配置

    <filter>
<!--过滤器名称-->
<filter-name>Set Character Encoding</filter-name>
<!--过滤器处理类-->
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!--初始化参数-->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Set Character Encoding</filter-name>
<!--拦截路径-->
<url-pattern>/*</url-pattern>
</filter-mapping>

打开CharacterEncoding类,该类为request设置字符编码,打开类继承图

CharacterEncodingFilter重写父类OncePerRequestFilter的doFilterInterval方法

forceRequestEncoding为True

188行获取编码

191行为request设置编码

194行为response设置编码

197行调用FilterChain

打开CharcterEncodingFilter的父类OncePerRequestFilter,OncePerRequestFilter保证请求分发执行一次Filter

CharcterEncodingFilter实现Filter接口的doFilter方法,打开之

96行获取属性,该属性表明这个方法是否被过滤

98行hasAlreadyFilteredAttribute表明这个方法是否被过滤;skipDispatch方法判断是否跳过分发;shouldNotFilter方法默认返回False,被子类重写,然而,CharacterEncodingFilter类并未重写,ForwardedHeaderFilter重写该方法,暂时不看

101行直接调用FilterChain的doFilter方法,FilterChain部分

98行的上述条件都不满足,则进入105行,为request设置“已过滤”标识

107行调用子类CharacterEncodingFilter的doFilterInterval方法

继续深入,打开OncePerRequestFilter类的父类GenericFilterBean类

重点看init方法,该方法启动服务才会进入,主要负责获取web.xml里配置的参数

180行获取FilterConfig

filterClass和filterName是我们在web.xml配置的属性

184行获取<init-param>里属性

185行声明BeanWrapper,一个通用的JavaBean接口,封装了setget方法,在SpringMVC源码阅读:属性编辑器、数据绑定我已经讲过

186行声明资源加载器,加载classpath和文件系统

189行给属性设置True

199行initFilterBean方法被子类重写,用于补充初始化方法,OncePerRequestFilter未重写,我们暂时不看

3.自定义过滤器

现在Web项目基本都是前后端分离的,不可避免地要解决跨域问题。看Filter接口继承图,发现CorsFilter可以处理跨域,但是并不方便

什么是跨域?

前端地址和后台地址的

IP一致,端口不一致

IP不一致,端口不一致

IP不一致,端口一致

都属于跨域范畴

前端代码如下

<!DOCTYPE html>
<html> <head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title></title>
<!-- <link rel="stylesheet" href=""> -->
<style>
</style>
</head> <body>
<script></script>
<script type="text/javascript" src="jquery-3.1.1.min.js"></script>
<script type="text/javascript">
$.ajax({
url: "http://localhost:8080/springmvcdemo/employee/detail/8",
data: {},
success: function (data)
{
console.log(html)
},
dataType: "json"
});
</script> </body> </html>

将前端代码地址设置为http://localhost:1234,VSCode可以做,不赘述。后台代码地址http://localhost:8080,因为端口不一致,形成了跨域,不做跨域处理,直接访问有如下效果

现在我们来自定义过滤器解决跨域问题

3.1 自定义过滤器继承OncePerRequestFilter

web.xml

    <filter>
<!--过滤器名称-->
<filter-name>CorsFilter</filter-name>
<!--过滤器处理类-->
<filter-class>org.format.demo.custom.ExtendedCorsFilter</filter-class>
</filter> <filter-mapping>
<filter-name>CorsFilter</filter-name>
<!--拦截路径-->
<url-pattern>/*</url-pattern>
</filter-mapping>

ExtendedCorsFilter.java,重写doFilterInterval方法

public class ExtendedCorsFilter extends OncePerRequestFilter{

    @Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
response.addHeader("Access-Control-Allow-Origin", "*");
response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
response.addHeader("Access-Control-Allow-Headers", "Content-Type");
filterChain.doFilter(request,response);
} }

此时再运行前端代码,已经成功请求到结果

Headers设置成功

3.2 自定义过滤器实现Filter接口

web.xml配置如下

 <filter>
<!--过滤器名称-->
<filter-name>CorsFilter</filter-name>
<!--过滤器处理类-->
<filter-class>org.format.demo.custom.ImplementedCorsFilter</filter-class>
<!--初始化参数-->
<init-param>
<param-name>allowedOrigins</param-name>
<param-value>*</param-value>
</init-param>
<init-param>
<param-name>allowedMethods</param-name>
<param-value>*</param-value>
</init-param>
<init-param>
<param-name>allowedHeaders</param-name>
<param-value>*</param-value>
</init-param>
</filter>

ImplementedCorsFilter.java

public class ImplementedCorsFilter implements Filter{
//允许的请求域
private String allowedOrigins;
//允许的请求方法
private String allowedMethods;
//允许的请求头
private String allowedHeaders; @Override
public void init(FilterConfig filterConfig) throws ServletException {
this.allowedOrigins = filterConfig.getInitParameter("allowedOrigins");
this.allowedMethods = filterConfig.getInitParameter("allowedMethods");
this.allowedHeaders = filterConfig.getInitParameter("allowedHeaders");
} @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletResponse rsp = (HttpServletResponse) response;
rsp.setHeader("Access-Control-Allow-Origin", allowedOrigins);
rsp.setHeader("Access-Control-Allow-Methods", allowedMethods);
rsp.setHeader("Access-Control-Allow-Headers", allowedHeaders); chain.doFilter(request, response);
} @Override
public void destroy() { }
}

相比继承OncePerRequestFilter方法,实现Filter接口可以通过重写init方法获取web.xml属性值,效果一致

4.过滤器(Filter)和拦截器(Interceptor)的区别

我在SpringMVC源码阅读:拦截器详细讲解了SpringMVC拦截器的工作原理

过滤器先于拦截器执行,后于拦截器执行结束

4.1 过滤器:

  依赖于servlet容器。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的是用来做一些过滤操作,获取我们想要获取的数据.

  比如:在过滤器中修改字符编码;在过滤器中修改HttpServletRequest的一些参数,包括:过滤低俗文字、危险字符等

4.2 拦截器:

  依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架。在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用。由于拦截器是基于Web框架的调用。

  因此可以使用spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。但是缺点是只能对controller请求进行拦截,对其他的一些比如直接访问静态资源的请求则没办法进行拦截处理。

总结:业务中尽量使用基于方法的拦截器,在进行一些需要统一处理的业务可以使用基于Servlet的过滤器

5.总结:

Filter接口用来执行过滤任务

CompositeFilter实现filter,用到了组合设计模式

抽象类GenericFilterBean实现Filter接口,负责解析web.xml的Filter的init-param中参数,是所有过滤器的父类。init方法解析web.xml的参数

抽象类OncePerRequestFilter继承GenericFilterBean,doFilter方法根据hasAlreadyFilteredAttribute判断是否执行过滤

CharacterEncodingFilter重写父类OncePerRequestFilter的doFilterInterval方法,调用FilterChain的doDilter方法执行过滤逻辑

其他过滤器园友可以自行查看,不再赘述,demo源码

6.参考

https://docs.spring.io/spring/docs/4.3.7.RELEASE/spring-framework-reference/htmlsingle/#beans-beans-conversion

https://docs.spring.io/spring/docs/current/javadoc-api/

https://github.com/spring-projects/spring-framework

文中难免有不足,欢迎指正

SpringMVC源码阅读:过滤器的更多相关文章

  1. SpringMVC源码阅读系列汇总

    1.前言 1.1 导入 SpringMVC是基于Servlet和Spring框架设计的Web框架,做JavaWeb的同学应该都知道 本文基于Spring4.3.7源码分析,(不要被图片欺骗了,手动滑稽 ...

  2. SpringMVC源码阅读:属性编辑器、数据绑定

    1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...

  3. SpringMVC源码阅读:拦截器

    1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...

  4. SpringMVC源码阅读:核心分发器DispatcherServlet

    1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将介绍SpringMVC的核 ...

  5. SpringMVC源码阅读:定位Controller

    1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码分析,弄清楚Spr ...

  6. SpringMVC源码阅读:Controller中参数解析

    1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...

  7. SpringMVC源码阅读:Json,Xml自动转换

    1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...

  8. SpringMVC源码阅读:视图解析器

    1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...

  9. SpringMVC源码阅读:异常解析器

    1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...

随机推荐

  1. Linux环境部署项目引起Out of Memory Error: PermGen Space的解决方案

    1. 背景 前几天,在搭建项目时遇到到一些问题,现在整理记录一下. Linux环境:Red Hat Enterprise Linux Server release 6.4: # 查看命令cat /et ...

  2. ABP框架系列之二十:(Dependency-Injection-依赖注入)

    What is Dependency Injection If you already know Dependency Injection concept, Constructor and Prope ...

  3. s31 zabbix监控企业级监控

    zabbix 简介与监控 zabbix安装部署 zabbix配置主机监控 zabbix 自定义监控 zabbix 配置报警方式 网站服务全面监控 zabbix 自动发现与snmp监控

  4. thinkphp 5.1 同时选中多个文件上传

    <form id="ajaxform" enctype="multipart/form-data" class="form"> ...

  5. Jersey RESTful WebService框架学习(八)文件下载防乱码

    最近在做下载时候  不同浏览器下载的文件一直出现乱码,不知道怎么设置文件的编码,百度许久,找到一个解决办法如下 /** * 文件下载 * @param request * @return */ @GE ...

  6. oracle-查询-时间条件查询

    select * from 表名 where date =to_date('时间','yyyy-dd-mm');

  7. python实战提升--1

    #python实战提升 1. 如何在列表.字典.集合中根据条件筛选数据? python中for _ in range(10)与for i in range(10)有何区别 下划线表示 临时变量, 仅用 ...

  8. unigui结合JS方法记录

    在js中界面上所有组件都当成html里来控制 .控制按钮事件  document.getElementById(MainForm.UniButton4.getId()).click(); 这个方法让J ...

  9. 背水一战 Windows 10 (74) - 控件(控件基类): UIElement - 与 CanDrag 相关的事件, 与 AllowDrop 相关的事件

    [源码下载] 背水一战 Windows 10 (74) - 控件(控件基类): UIElement - 与 CanDrag 相关的事件, 与 AllowDrop 相关的事件 作者:webabcd 介绍 ...

  10. 使用autogen工具生成Makefile遇到问题解决思路

    使用autogen工具生成Makefile,最新的应用程序很多都使用autogen,本着知行合一的精神 最近有空也研究了一下该工具的使用,详细步骤请参考文档: http://blog.csdn.net ...