文章目录
Fiter是什么:
Filter:一个实现了特殊接口(Filter)的Java类. 实现对请求资源(jsp,servlet,html,)的过滤的功能. 过滤器是一个运行在服务器的程序, 优先于请求资源(Servlet或者jsp,html)之前执行. 过滤器是javaweb技术中最为实用的技术之一
简而言之,就是在Servlet之前接收到用户发送的request,然后我们可以检查参数,对编码等等进行初始化的设置,比如判断用户是否有权限查看该网站的该网址下的内容…
Filter基本配置
配置Filter需要在 web.xml中声明,下面是一个模板
<filter>
<filter-name>Filter01</filter-name>
<filter-class>com.example.demo.Filter01</filter-class>
</filter>
<filter-mapping>
<filter-name>Filter01</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
代表作为开发者,我们创建这个Filter的全类名,目的是在运行web应用的时候,web应用容器可以发现这个Filter
代表该Filter 所过滤的网址。什么意思呢,还记得我们创建Servlet需要写的声明和这个很类似吗
对于
<servlet>
<servlet-name>Servlet1</servlet-name>
<servlet-class>com.example.demo.Servlet1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Servlet1</servlet-name>
<url-pattern>/Servlet</url-pattern>
</servlet-mapping>
对于上面的代码,表示用户创建web应用的Servlet的全类名
而代表其所对应的url,从上面这个模板可以看到它的,其对应的完整url地址应该是服务器url+标签内的值
那么结果应该是http://localhost:8080/demo_war/Servlet1
那么再回到上面说到的,每个Setvlet都有一个对应的url,而Filter它的代表的是所过滤的url,其完整地址也是服务器的URL+此标签的值。
四种匹配模式
Filter并不是专用于过滤某个Servlet,我们可以通过配置,来自定义其所拦截的请求,联系实际。比如说淘宝,你打开淘宝网站,并没有强制你不登陆无法访问商品,而是你购买的时候必须要登录。当用户仅仅是浏览时,通过过滤器可以被放行,但是用户没有登录想要购买时,就无法放行此请求给支付模块,而是强制用户登录才能进入支付模块
1. 精确匹配
指定被拦截资源的完整路径:
<!-- 配置Filter要拦截的目标资源 -->
<filter-mapping>
<!-- 指定这个mapping对应的Filter名称 -->
<filter-name>FilterDemo01</filter-name>
<!-- 通过请求地址模式来设置要拦截的资源 -->
<url-pattern>/demo01</url-pattern>
</filter-mapping>
上述例子表示要拦截映射路径为/demo01
的这个资源
2. 模糊匹配
相比较精确匹配,使用模糊匹配可以让我们创建一个Filter就能够覆盖很多目标资源,不必专门为每一个目标资源都创建Filter,提高开发效率。
在我们配置了url-pattern为/user/*之后,请求地址只要是/user开头的那么就会被匹配。
<filter-mapping>
<filter-name>Target02Filter</filter-name>
<!-- 模糊匹配:前杠后星 -->
<!--
/user/demo01
/user/demo02
/user/demo03
/demo04
-->
<url-pattern>/user/*</url-pattern>
</filter-mapping>
极端情况:/*匹配所有请求
3. 扩展名匹配
<filter>
<filter-name>Target04Filter</filter-name>
<filter-class>com.atguigu.filter.filter.Target04Filter</filter-class>
</filter>
<filter-mapping>
<filter-name>Target04Filter</filter-name>
<url-pattern>*.png</url-pattern>
</filter-mapping>
上述例子表示拦截所有以.png
结尾的请求
4. 匹配Servlet名称
<filter-mapping>
<filter-name>Target05Filter</filter-name>
<!-- 根据Servlet名称匹配 -->
<servlet-name>Target01Servlet</servlet-name>
</filter-mapping>
Filter的基本作用
Filter的作用是对目标资源(Servlet,jsp)进行过滤,其应用场景有: 登录权限检查,解决网站乱码,过滤敏感字符等等
例如:如果一个电商web,我们想使用加入购物车功能必须要判断用户是否登录,如果没有filter的话,就需要在servlet中进行大量的无用的测试代码,非常影响效率。
这时fitler 就出现了,我们在filter-mapping中的url-partern中指定要过滤的文件(可以多个url-partern),设置后,那么在访问该servlet之前,就需要通过一个filter的检测,我们可以在该处检测一些变量和权限,如果可通过就使用chain.doFilter(req,res)
用 Filter 解决中文乱码问题案例:
实现请求在到底目标Servlet之前解决请求参数乱码问题
- 创建一个Servlet,并在web.xml中编写servlet配置
假如是右键自动生成的Servlet,则不用手动在 web.xml中配置
<servlet>
<servlet-name>servletDemo01</servlet-name>
<servlet-class>com.ggzx.ServletDemo01</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servletDemo01</servlet-name>
<!--这里是-->
<url-pattern>/ServletDemo01</url-pattern>
</servlet-mapping>
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 日期2022-2-6 09:28
* @author ggzx
*/
@WebServlet(name = "ServletDemo01", value = "/hello-servlet")
public class ServletDemo01 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
System.out.println("ServletDemo01接收到了一个请求..."+username);
}
}
前端界面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<form action="hello-servlet" method="post">
用户名<input type="text" name="username"/><br/>
<input type="submit"/>
</form>
</body>
</html>
现在来看没有使用Filter来设置字符集的情况
这里是Servlet直接接收到的参数,可以看见,在接收到汉字的参数,其实是有问题的。
下面来配置使用Filter:
web.xml
<filter>
<filter-name>Filter01</filter-name>
<filter-class>com.example.demo.Filter01</filter-class>
</filter>
<filter-mapping>
<filter-name>Filter01</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
public class Filter01 implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//解决请求参数的乱码
HttpServletRequest request = (HttpServletRequest) req;
request.setCharacterEncoding("UTF-8");
//每次有请求被当前filter接收到的时候,就会执行doFilter进行过滤处理
System.out.println("EncodingFilter接收到了一个请求...");
//这句代码表示放行
chain.doFilter(req, resp);
}
@Override
public void init(FilterConfig config) throws ServletException {
}
}
我们配置的Filter会自动过滤web.xml中/*所表示的路径下的所有Servlet。当用户发送表单后,首先被Filter拦截,在执行了解决请求参数乱码的问题后再将请求放给Servlet,再使用了这样一个设置编码的Filter之后,就不需要在每个Servlet中设置编码了。
一个小问题:用户发送了request,被filter拦住设置了字符集后,Servlet为什么能通过request得到不乱码的汉字内容,这是为什么
来看一下Servlet中的doget方法
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
System.out.println("ServletDemo01接收到了一个请求..."+username);
}
来看一下Filter中的doget方法
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("Filter01:过滤");
chain.doFilter(request, response);
}
可以看见,他们两个同名的参数(类型不同)request,这是用户传过来的请求,里面包含了很多的用户信息,我们在Filter中设置的request和Servlet的 request 可能继承同一个接口,或者这两个接口就有继承关系
通过查找源码可以看见,HttpServletRequest 继承 ServletRequest,用户发来的 request 经过 Filter 时,继承的是父接口 ServletRequest,在Filter之中对 request 进行处理后,进入到 Servlet 时,继承的接口是 HttpServletRequest ,子接口的功能肯定是多于父接口的,所以在上面的 Filter 的 doFilter 中, HttpServletRequest request = (HttpServletRequest) req;
执行了这段代码,直接将功能少的 ServletRequest 强转成了功能更多的HttpServletRequest ,然后再执行 chain.doFilter(req, resp);
执行这段代码之后,就会将request请求发给Servlet
总结这个问题:即filter和servlet方法中的 req都是同一个对象。Filter中的请求对象是ServletRequest对象,而Servelet中的是HttpServletRequset对象,说明在过滤后发生了强转,其中HttpServletRequest转换成ServletRequest对象,子接口HttpServletRequest比父接口ServletRequest,功能更多,所以强转的时候不会有问题
Filter生命周期
生命周期阶段 | 执行时机 | 生命周期方法 |
---|---|---|
创建对象 | Web应用启动时 | init方法,通常在该方法中做初始化工作 |
拦截请求 | 接收到匹配的请求 | doFilter方法,通常在该方法中执行拦截过滤 |
销毁 | Web应用卸载前 | destroy方法,通常在该方法中执行资源释放 |
如果有多个Filter,构成了过滤器链,那么其各自的执行顺序又是怎么样的
此部分内容参考【SpringMVC】Filter过滤器、AOP切面类、Interceptors拦截器各自的执行顺序中的多个过滤器部分