深入了解过滤器、监听器(中)
第四章 Filter的生命周期
我们在前面的文章学习过servlet生命周期,并且上面,我们分析了Filter的执行流程。我们有没有想过,我们并没有向Filter发送请求,请求是怎么进入到doFilter()中的呢?Filter类的对象是在什么时候创建的呢?创建之后又是怎么工作的呢?最后是在什么时候销毁的呢?这些问题就是Filter的生命周期问题。接下来我们要学习过滤器filter的生命周期,和servlet的生命周期差不多。
1.filter生命周期代码
接下来我们可以通过以下代码对Filter的生命周期问题进行验证:
【验证Filter的生命周期】LifecycleFilter.java
@WebFilter("/lifecycle")
public class LifecycleFilter implements Filter {
@Override
public void destroy() {
System.out.println("被销毁了。。。");
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
//your code....
System.out.println("doFilter()被执行了。。。");
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig config) throws ServletException {
System.out.println("初始化了。。。");
}
public LifecycleFilter() {
System.out.println("构造方法被执行了。。。");
}
}
重启服务器:在浏览器输入访问过滤器地址,并查看控制台。
http://localhost:8080/lifecycle
【结论】
- Filter类的对象是在服务器启动的时候创建的;
- Filter类对象创建完成后会调用init()方法;
- 每当一个请求的路径是满足过滤器的配置路径,那么就会执行一次过滤器的doFilter方法 (类似于service方法)
- 当服务器停止的时候,销毁过滤器,执行过滤器的destory方法
2.doFilter()方法
doFilter()方法是拦截请求的方法,只要一个请求符合Filter拦截路径,都会被doFilter()方法拦截。doFilter()方法有3个参数,这3个参数的作用分别如下:
参数 | 说明 |
---|---|
ServletRequest req | ServletRequest是HttpServletRequest类的父接口,其中包含了被拦截的请求的信息 |
ServletResponse resp | ServletResponse是HttpServletResponse类的父接口,其中包含了被拦截的响应的信息 |
FilterChain chain | FilterChain给Filter提供了放行的方法。chain.doFilter(); |
FilterChain 是一个接口,里面有doFilter(req,resp)方法,作用是将浏览器提交请求对象和响应对象传到过滤器的资源里面去,所以doFilter()方法具有放行的作用。
3.destory()方法
过滤器的销毁方法。服务器停止的时候,销毁过滤器,调用一次destory方法。
【测试】在destory()方法中输出一句话,如下:
@Override
public void destroy() {
System.out.println("服务器关闭");
System.out.println("LifecycleFilter的destory方法执行了.......................");
}
【关闭tomcat】查看结果
【结论】
- 过滤器是在服务器停止时销毁的;
- 过滤器销毁后会调用destory()方法;
【小结】Filter生命周期小结
- 当服务器启动的时候,过滤器就被初始化了,执行过滤器的init方法
- 每当一个请求的路径是满足过滤器的配置路径,那么就会执行一次过滤器的doFilter方法
- 当服务器停止的时候,销毁过滤器,执行过滤器的destory方法
第五章 Filter的映射路径
映射路径指的是当前这个Filter需要拦截的路径。这个路径的配置方式有3种:
- 精确匹配:匹配指定的urlpattern;
- 模糊匹配:匹配复合条件的一类路径;
- 多路径匹配:一个filter匹配多个资源;
1、精确匹配
精确匹配,要求访问的资源路径必须与Filter的urlPatterns中的配置一致。如下:
精确匹配 | 说明 |
---|---|
urlPatterns = "/index.jsp" | 拦截对index.jsp页面的请求 |
urlPatterns = "/loginServlet" | 拦截对loginServlet的请求 |
urlPatterns = "/a.jsp" | 拦截对a.jsp的请求 |
2、模糊匹配
有时候,我们需要对这个服务器中的所有请求都有所特殊处理,比如处理post请求的乱码问题。我们就需要对所有的请求都设置乱码处理。这时候,就需要对所有的请求进行拦截。如果通过上面的精确匹配模式,需要配置很多的请求,这样就比较麻烦了。如果通过urlPatterns的模糊匹配,就能够对这些请求进行统一处理。
模糊匹配主要有以下3种写法:
模糊匹配 | 使用示例 | 说明 |
---|---|---|
urlPatterns = "/*" | urlPatterns = "/*" | 对所有的请求都进行拦截 |
urlPatterns = "/xxx/*" | urlPatterns = "/user/*" | 对映射路径中/user下的所有资源进行拦截 |
urlPatterns = "*.xxx" | urlPatterns = "*.jsp" | 对指定后缀的请求进行拦截 不加/ |
使用场景
今天电商狂欢日,产品经理要求对所有购买的电脑进行打9折
http://localhost:8080/computer/dell
http://localhost:8080/computer/xiaomi
http://localhost:8080/computer/thinkpad
效果:我们只要过滤器中路径设置为:/computer/*,那么任何电脑的请求都会被过滤器拦截。
使用场景
网站有很多种请求:浏览商品,购买商品,删除商品;用户登录,修改用户信息,注册用户信息。
我们可以将请求分为两类:xxx.action和xxx.do
这样可以分清楚网站请求的类别。
效果:我们只要在过滤器中设置路径为:*.action,那么,所有以.action结尾的请求都会被过滤器拦截。
代码演示如下:
对所有的请求都进行拦截:
@WebFilter("/*")
public class OneFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
System.out.println("OneFilter执行了。。。");
// chain.doFilter(request, response);
}
public void init(FilterConfig config) throws ServletException {
}
}
对映射路径中/user下的所有资源进行拦截:
@WebFilter("/user/*")
public class TwoFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
System.out.println("TwoFilter执行了。。。。。");
// chain.doFilter(request, response);
}
public void init(FilterConfig config) throws ServletException {
}
}
对指定后缀的请求进行拦截:
@WebFilter("*.action")
public class ThrFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
System.out.println("ThrFilter执行了。。。。");
// chain.doFilter(request, response);
}
public void init(FilterConfig config) throws ServletException {
}
}
3、多个路径匹配
一个过滤器可以匹配多个路径
xml实现方式:
注解实现方式:
温馨提示:路径没有/,Tomcat会报错!
第六章 过滤器的拦截方式
什么是拦截方式:对浏览器或服务器过来的请求进行拦截。
1.通过浏览器发送的请求:默认的
2.请求转发:默认不拦截
案例:演示过滤器的拦截方式。分别从浏览器和服务器转发过来的请求。
【代码如下】
HelloServlet:
@WebServlet("/helloServlet")
public class HelloServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("helloServlet 接收了请求。。。。。");
//响应数据
response.getWriter().print("hello");
}
}
HelloFilter:
@WebFilter("/helloServlet")
public class HelloFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
System.out.println("HelloFilter拦截了请求。。。。");
chain.doFilter(request, response);
System.out.println("HelloFilter拦截了响应。。。。");
}
public void init(FilterConfig config) throws ServletException {
}
}
1. 默认的拦截方式
只对从浏览器直接发送过来的请求进行拦截,值:REQUEST
在浏览器中直接访问HelloServlet,看过滤器是否拦截。
显示是拦截了。
2. 来自转发的请求被拦截
- 在filter.jsp转发到HelloServlet,查看过滤器是否拦截。
filter.jsp页面代码:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%-- <jsp:forward page="/helloServlet"/>--%>
<%
request.getRequestDispatcher("/helloServlet").forward(request, response);
%>
</body>
</html>
在浏览器上直接访问filter.jsp页面。
idea控制台:
我们发现直接访问jsp,然后在jsp页面转发访问helloServlet,过滤器没有拦截。
因为过滤器默认只拦截从浏览器的直接请求。不拦截请求转发的请求,如果希望过滤器对请求转发的请求进行拦截,我们必须按照如下进行配置。
2.过滤器的配置
配置方式1:web.xml
<!--配置一个过滤器-->
<filter>
<!--过滤器名字-->
<filter-name>hello</filter-name>
<!--过滤器的类全名-->
<filter-class>com.ita.demo01.HelloFilter</filter-class>
</filter>
<filter-mapping>
<!--过滤器名字,必须与上面的名字相同-->
<filter-name>hello</filter-name>
<!--过滤的地址-->
<url-pattern>/helloServlet</url-pattern>
<!--配置拦截方式,为转发-->
<dispatcher>FORWARD</dispatcher>
<!--配置拦截方式,浏览器的请求-->
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
说明:
1)在web.xml中配置之后必须去掉过滤器中的注解
//@WebFilter("/helloServlet")
public class HelloFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
System.out.println("HelloFilter拦截了请求。。。。");
chain.doFilter(request, response);
System.out.println("HelloFilter拦截了响应。。。。");
}
public void init(FilterConfig config) throws ServletException {
}
}
2)如果只配置 FORWARD 只会拦截转发的请求,必须还得配置 REQUEST同时还得拦截浏览器的请求。
配置方式2:注解
@WebFilter(urlPatterns = "/helloServlet", dispatcherTypes = {DispatcherType.FORWARD,DispatcherType.REQUEST})
public class HelloFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
System.out.println("HelloFilter拦截了请求。。。。");
chain.doFilter(request, response);
System.out.println("HelloFilter拦截了响应。。。。");
}
public void init(FilterConfig config) throws ServletException {
}
}
注意:去掉web.xml文件中的拦截代码。
3 拦截类型小结
拦截类型 | 作用 |
---|---|
REQUEST | 只对直接来源于浏览器的请求才过滤(包含重定向) |
FORWARD | 对服务器的转发进行过滤 |
第七章 Filter应用案例(练习一下叭)
1、Filter统一解决post请求乱码问题
8.0版本以上的tomcat为我们解决了get请求的乱码问题。我们需要自己处理post请求的乱码问题,因为我们的一个项目中可能有很多的请求。当然,我们可以在每一个Servlet中都对post的乱码进行处理。但是,Filter给我们提供了一个更加便捷的方式。我们自定义一个Filter类,拦截所有的请求,然后对这些请求进行post乱码处理即可。
【参考代码】:
1)页面:register.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>form</title>
</head>
<body>
<form action="/registerServlet" method="post">
用户名:<input type="text" name="name"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
2)后台 servlet代码:
RegisterServlet.java
@WebServlet("/registerServlet")
public class RegisterServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//处理请求乱码
//request.setCharacterEncoding("utf-8");
//处理响应乱码
//response.setContentType("text/html;charset=utf-8");
/*
在这里我们需要处理post请求乱码和响应乱码,如果多个servlet,那么我们就会每个servlet
中都会进行处理,这样代码会冗余,我们可以考虑使用过滤器来解决处理乱码问题
*/
//获取用户数据
String name = request.getParameter("name");
String password = request.getParameter("password");
System.out.println("name = " + name);
response.getWriter().println("哈哈,乱码解决楼!");
}
}
说明:上述代码中我们需要处理post请求乱码和响应乱码,如果多个servlet,那么我们就会每个servlet中都会进行处理,这样代码会冗余,我们可以考虑使用过滤器来解决处理乱码问题。即将处理乱码的代码放到过滤器中来处理。
3)过滤器EncodingFilter中的代码:
@WebFilter("/*")
public class EncodingFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
//统一处理post请求乱码
request.setCharacterEncoding("utf-8");
//统一处理响应乱码
response.setContentType("text/html;charset=utf-8");
//放行
chain.doFilter(request, response);
}
public void init(FilterConfig config) throws ServletException {
}
}
2、过滤器案例--登录权限校验
【需求】
- 登录之后:能够访问hack.html,然后下载黑客视频资源,成为NB黑客;
- 未登录:跳转到登录页面,并提示"请先登录";
- 我们对hack.html页面进行过滤,
详细描述:
1.页面:hack.html 访问这个页面要求必须先登录,不登录不能访问,使用过滤器书写代码让其跳转到登录页面login.html.
2.登录LoginServlet中获取用户名和密码,存放到User对象中,然后在session中。最后重定向到hack.html
3.使用一个过滤器对hack.html 进行拦截,先从session中获取用户信息,如果没有登录,获取的是null。跳转到登录页面。如果不为null,说明登录了,可以访问hack.html。
下载资源页面:hack.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>黑客。。</h1>
<a href="javascript:;">下载黑客资源</a>
</body>
</html>
页面login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/loginServlet" method="post">
用户名:<input type="text" name="name"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
javabean,User类:
public class User {
private String name;
private String password;
public User(String name, String password) {
this.name = name;
this.password = password;
}
public User() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"name='" + name + ''' +
", password='" + password + ''' +
'}';
}
}
LoginServlet代码:
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取用户名和密码
String name = request.getParameter("name");
String password = request.getParameter("password");
User user = new User(name,password);
System.out.println("user = " + user);
//将用户信息保存session中
request.getSession().setAttribute("user",user);
response.sendRedirect("/hack.html");
}
}
LoginFilter代码:
@WebFilter("/hack.html")
public class LoginFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
//获取用户信息
User user = (User) request.getSession().getAttribute("user");
if (user == null) {
//没有登陆
response.sendRedirect("/login.html");
return;
} else {
//放行。。
chain.doFilter(request, response);
}
}
public void init(FilterConfig config) throws ServletException {
}
}
转载自:https://juejin.cn/post/7242717749905260600