web基础5-servlet入门4(Filter和Listener)
web基础5-servlet入门4(Filter和Listener)
一、Filter
1.1 FIter介绍
1.1.1 Filter的作用
后面的Spring MVC和Shiro框架都是用filter实现
Filter:过滤器,用来过滤网站的数据
- 为程序员减少代码的机制
- 处理中文乱码
- 登陆应用
1.1.2 不使用filter
场景:处理乱码
原始方式是通过servlet里设置编码。这样请求多少个servlet,就需要设置多少次。显然太笨了。
package com.happy.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;
public class SuccessServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("进入successservlet");
// response.setCharacterEncoding("utf-16");
// 原始方式:原来通过servlet里设置编码
// 这样请求多少个servlet,就需要设置多少次
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.write("中文乱码!");
out.write("进入successservlet success!");
System.out.println("准备返回最初调用的registrationServlet");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
1.1.3 使用filter
-
过滤中的所有代码,在过滤特定请求的时候都会执行
-
必须要让过滤器继续通行:
chain.doFilter(request,response)
加了过滤器以后
过滤链:
1.2 Fitler开发步骤
1.2.1 导入相关依赖jar包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.happy</groupId>
<artifactId>MvcServlet</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>MvcServlet Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
</dependency>
<!-- jstl表带式的依赖,依赖于标签-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
</dependencies>
</project>
1.2.2 编写过滤器
实现相关接口filter
package com.happy.filter;
import javax.servlet.*;
import java.io.IOException;
public class CharacterEncodingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("CharacterEncodingFilter初始化===");
}
// 链:chain=>可以有多个filter
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
servletRequest.setCharacterEncoding("utf-8");
servletResponse.setCharacterEncoding("utf-8");
servletResponse.setContentType("text/html;charset=utf-8");
System.out.println("CharaterEncodingFilter执行完毕前");
// 让我们的请求继续走,如果不写,请求会在这里被拦截停止
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("CharaterEncodingFilter执行完毕后");
}
@Override
public void destroy() {
System.out.println("CharacterEncodingFilter销毁===");
}
}
1.2.3 web.xml注册过滤器
注册过滤器,并设置过滤器的作用范围
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<error-page>
<error-code>404</error-code>
<!-- 第一个/代表当前项目-->
<location>/error/404.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<!-- 第一个/代表当前项目-->
<location>/error/500.jsp</location>
</error-page>
<servlet>
<servlet-name>SuccessServlet</servlet-name>
<servlet-class>com.happy.servlet.SuccessServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>SuccessServlet</servlet-name>
<url-pattern>/filter/successtest</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>SuccessServlet</servlet-name>
<url-pattern>/nofilter/successtest</url-pattern>
</servlet-mapping>
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>com.happy.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<!-- 只要是目标url地址的servlet请求都会走该filter-->
<url-pattern>/filter/*</url-pattern>
</filter-mapping>
</web-app>
1.2.4 执行效果
访问被过滤的请求地址:
访问没有过滤的请求地址(同一个servlet):可以看到未被过滤的servlet返回乱码
1.3 Filter的生命周期
1.3.1 Filter初始化
如下图,只要完成在web.xml注册过滤器以后,在web服务器启动的时候,就已经初始化,随时等待过滤对象出现!而servlet初始化为第一次访问时
24-Apr-2022 18:38:34.956 信息 [main] org.apache.catalina.startup.Catalina.start [104]毫秒后服务器启动
Connected to server
[2022-04-24 06:38:35,019] Artifact MvcServlet:war: Artifact is being deployed, please wait...
24-Apr-2022 18:38:36.199 信息 [RMI TCP Connection(3)-127.0.0.1] org.apache.jasper.servlet.TldScanner.scanJars 至少有一个JAR被扫描用于TLD但尚未包含TLD。 为此记录器启用调试日志记录,以获取已扫描但未在其中找到TLD的完整JAR列表。 在扫描期间跳过不需要的JAR可以缩短启动时间和JSP编译时间。
CharacterEncodingFilter初始化===
[2022-04-24 06:38:36,295] Artifact MvcServlet:war: Artifact is deployed successfully
1.3.2 Filter的销毁
Filter在服务器关闭的时候进行销毁。这个同servlet一样。
24-Apr-2022 18:39:27.573 信息 [main] org.apache.catalina.core.StandardService.stopInternal 正在停止服务[Catalina]
24-Apr-2022 18:39:27.589 信息 [main] org.apache.coyote.AbstractProtocol.stop 正在停止ProtocolHandler ["http-nio-8080"]
SuccessServlet销毁了===
CharacterEncodingFilter销毁===
24-Apr-2022 18:39:27.596 信息 [main] org.apache.coyote.AbstractProtocol.destroy 正在摧毁协议处理器 ["http-nio-8080"]
Disconnected from server
1.3.3 Filter的执行
24-Apr-2022 18:42:16.432 信息 [Catalina-utility-2] org.apache.catalina.startup.HostConfig.deployDirectory Web应用程序目录[D:\software\tomcat\apache-tomcat-9.0.62\webapps\manager]的部署已在[124]毫秒内完成
================================================
访问了http://localhost:8080/mvc/filter/successtest
================================================
SuccessServlet初始化了===
进入successservlet
准备返回最初调用的registrationServlet
================================================
访问了http://localhost:8080/mvc/filter/successtest
================================================
CharaterEncodingFilter执行完毕前
进入successservlet
准备返回最初调用的registrationServlet
CharaterEncodingFilter执行完毕后
1.4 Filter vs Servlet
Filter | Servlet | ||
---|---|---|---|
相同 | web.xml注册 | 需要 | 需要 |
注册方式 | 都需要mapping | 都需要mapping | |
销毁时间 | 服务器关闭时 在Servlet之后 |
服务器关闭时 在filter之前 |
|
不同 | 初始化时间 | 在服务器启动时 | 第一次访问servlet |
继承或实现 | implements Filter | extends HttpServlet | |
重写核心方法 | doFilter | doGet,doPost |
二、Listener
2.1 Listener开发步骤
2.2.1 编写监听器
实现一个监听器的接口:
package com.happy.listener;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
//统计网站在线人数:统计session
public class OnlineCountListener implements HttpSessionListener {
// 创建session监听,看你的一举一动
// 一旦创建session就会触发一次这个事件
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
System.out.println(httpSessionEvent.getSession().getId());
ServletContext ctx = httpSessionEvent.getSession().getServletContext();
Integer onlineCount = (Integer) ctx.getAttribute("onlineCount");
if(onlineCount==null){
onlineCount=new Integer(1);
}else {
onlineCount=onlineCount+1;
}
ctx.setAttribute("onlineCount",onlineCount);
}
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
ServletContext ctx = httpSessionEvent.getSession().getServletContext();
// 方式一:手动销毁
System.out.println("session销毁======调用手动方式销毁");
System.out.println("关闭浏览器,并没有进入该方法");
httpSessionEvent.getSession().invalidate();
Integer onlineCount = (Integer) ctx.getAttribute("onlineCount");
if(onlineCount==null){
onlineCount=new Integer(0);
}else {
onlineCount=onlineCount-1;
}
ctx.setAttribute("onlineCount",onlineCount);
}
}
2.2.2 web.xml注册监听器
<!-- 注册监听器-->
<listener>
<listener-class>com.happy.filter.CharacterEncodingFilter</listener-class>
</listener>
注意:两种销毁session的方式:
- session.invalidate
sessionDestroyed()方法并不会在关闭浏览器时调用。所以最好用web.xml设置session timout
System.out.println("session销毁======调用手动方式销毁");
System.out.println("关闭浏览器,并没有进入该方法");
- web.xml设置session timeout
<session-config>
<session-timeout>1</session-timeout>
</session-config>
注意:session的cookie
-
默认情况下session的cookie的JSESSIONID虽然会随着浏览器关闭而cookie销毁,重新打开浏览器访问会重新生成新的cookie作为JSESSIONID。即cookie销毁,session不销毁。表面上重新打开浏览器发起,有了新的session
-
但服务器端的session并没有销毁,所以关闭浏览器,session并没有完全销毁,需要用上面两种方式销毁。
三、过滤器、监听器常见应用
3.1 登陆权限验证原理
- 用户登陆之后才能进入主页!
- 用户注销后就不能进入主页了!
实现主要逻辑:
- 用户登陆之后,向Session中放入用户的数据
- 进入主页success.jsp的时候要判断用户是否登陆,工作中要求在过滤中实现
- 方法一:直接在success.jsp中判断session是否有数据,不使用太low
方法二:加过滤器判断session是否有数据
3.2 实现步骤
登陆页面login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/servlet/login" method="get">
username:<input type="text" name="username"/>
password:<input type="password" name="password"/>
<input type="submit" value="提交"/>
</form>
<h1 style="color: orangered">
<%if(session.getAttribute("login")==null){
out.write("请重新登陆");
}%>
</h1>
</body>
</html>
登陆控制LoginServlet
表单提交携带数据到该servlet
package com.happy.servlet;
import com.happy.Utils.Constant;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//验证是否登陆成功
String username = request.getParameter("username");
String password = request.getParameter("password");
// request.getSession().setAttribute(Constant.LOGIN_SESSION,"no");
if(username.equals("admin") && password.equals("admin")){
// 成功则跳转到success.jsp页面
request.getSession().setAttribute(Constant.LOGIN_SESSION,"yes");
request.getSession().setAttribute("name",username);
request.getRequestDispatcher("/success.jsp").forward(request,response);
}else{
//失败,则跳转
// request.getSession().setAttribute(Constant.LOGIN_SESSION,"no");
request.getRequestDispatcher("/login.jsp").forward(request,response);
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
成功主页success.jsp
验证密码通过,跳转到success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${name}您好
<h1 style="color: blue">${status}</h1>
<%if(session.getAttribute("login")!=null&&session.getAttribute("login").equals("yes")) {
out.write("<h1 style='color: blue'>登陆成功</h1>");
}%>
<h1 style="color: orangered">
<a href="${pageContext.request.contextPath}/servlet/logout">注销</a>
</h1>
</body>
</html>
注销session的LogoutServlet
package com.happy.servlet;
import com.happy.Utils.Constant;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
public class LogoutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 方式一:销毁session
// request.getSession().invalidate();
// 方式二:移除相关session attribute
Object login = request.getSession().getAttribute(Constant.LOGIN_SESSION);
if(login!=null){
request.getSession().removeAttribute(Constant.LOGIN_SESSION);
response.sendRedirect(request.getContextPath()+"/login.jsp");
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
web.xml添加过滤器
为防止直接请求主页success.jsp,添加过滤器
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<error-page>
<error-code>404</error-code>
<!-- 第一个/代表当前项目-->
<location>/error/404.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<!-- 第一个/代表当前项目-->
<location>/error/500.jsp</location>
</error-page>
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>com.happy.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/servlet/login</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>LogoutServlet</servlet-name>
<servlet-class>com.happy.servlet.LogoutServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LogoutServlet</servlet-name>
<url-pattern>/servlet/logout</url-pattern>
</servlet-mapping>
<filter>
<filter-name>LoginFilter</filter-name>
<filter-class>com.happy.filter.LoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LoginFilter</filter-name>
<!-- 只要是目标url地址的servlet请求都会走该filter-->
<url-pattern>/success.jsp</url-pattern>
</filter-mapping>
<!-- 注册监听器-->
<listener>
<listener-class>com.happy.listener.OnlineCountListener</listener-class>
</listener>
<!-- <session-config>-->
<!-- <session-timeout>1</session-timeout>-->
<!-- </session-config>-->
</web-app>
编写过滤器LoginFilter
控制放行和重定向到重新登录页面
package com.happy.filter;
import com.happy.Utils.Constant;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class LoginFilter implements Filter {
public void init(FilterConfig config) throws ServletException {
}
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
// ServletRequest, HttpServletRequest
HttpServletRequest request1=(HttpServletRequest)request;
HttpServletResponse response1=(HttpServletResponse)response;
if( request1.getSession().getAttribute(Constant.LOGIN_SESSION)!=null&&request1.getSession().getAttribute(Constant.LOGIN_SESSION).equals("yes")){
chain.doFilter(request, response);
} else{
response1.sendRedirect(request1.getContextPath()+"/login.jsp");
}
}
}