Filter:一个实现了特殊接口(Filter)的Java类. 实现对请求资源(jsp,servlet,html,)的过滤的功能. 过滤器是一个运行在服务器的程序, 优先于请求资源(Servlet或者jsp,html)之前执行. 过滤器是javaweb技术中最为实用的技术之一
简而言之,就是在Servlet之前接收到用户发送的request,然后我们可以检查参数,对编码等等进行初始化的设置,比如判断用户是否有权限查看该网站的该网址下的内容…
配置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,我们可以通过配置,来自定义其所拦截的请求,联系实际。比如说淘宝,你打开淘宝网站,并没有强制你不登陆无法访问商品,而是你购买的时候必须要登录。当用户仅仅是浏览时,通过过滤器可以被放行,但是用户没有登录想要购买时,就无法放行此请求给支付模块,而是强制用户登录才能进入支付模块
指定被拦截资源的完整路径:
<!-- 配置Filter要拦截的目标资源 --> <filter-mapping> <!-- 指定这个mapping对应的Filter名称 --> <filter-name>FilterDemo01</filter-name> <!-- 通过请求地址模式来设置要拦截的资源 --> <url-pattern>/demo01</url-pattern> </filter-mapping>
上述例子表示要拦截映射路径为/demo01
的这个资源
相比较精确匹配,使用模糊匹配可以让我们创建一个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>
极端情况:/*匹配所有请求
<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
结尾的请求
<filter-mapping> <filter-name>Target05Filter</filter-name> <!-- 根据Servlet名称匹配 --> <servlet-name>Target01Servlet</servlet-name> </filter-mapping>
Filter的作用是对目标资源(Servlet,jsp)进行过滤,其应用场景有: 登录权限检查,解决网站乱码,过滤敏感字符等等
例如:如果一个电商web,我们想使用加入购物车功能必须要判断用户是否登录,如果没有filter的话,就需要在servlet中进行大量的无用的测试代码,非常影响效率。
这时fitler 就出现了,我们在filter-mapping中的url-partern中指定要过滤的文件(可以多个url-partern),设置后,那么在访问该servlet之前,就需要通过一个filter的检测,我们可以在该处检测一些变量和权限,如果可通过就使用chain.doFilter(req,res)
实现请求在到底目标Servlet之前解决请求参数乱码问题
<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中设置编码了。
来看一下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,功能更多,所以强转的时候不会有问题
生命周期阶段 | 执行时机 | 生命周期方法 |
---|---|---|
创建对象 | Web应用启动时 | init方法,通常在该方法中做初始化工作 |
拦截请求 | 接收到匹配的请求 | doFilter方法,通常在该方法中执行拦截过滤 |
销毁 | Web应用卸载前 | destroy方法,通常在该方法中执行资源释放 |
此部分内容参考【SpringMVC】Filter过滤器、AOP切面类、Interceptors拦截器各自的执行顺序中的多个过滤器部分