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

  1. 过滤中的所有代码,在过滤特定请求的时候都会执行

  2. 必须要让过滤器继续通行:

    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 登陆权限验证原理

  • 用户登陆之后才能进入主页!
  • 用户注销后就不能进入主页了!

实现主要逻辑:

  1. 用户登陆之后,向Session中放入用户的数据
  2. 进入主页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");
       }

    }
}

posted @ 2022-04-24 23:40  高兴518  阅读(171)  评论(0编辑  收藏  举报