JAVA WEB DAY 06_过滤器 & 监听器

过滤器 & 监听器

目标

  • 能够说出过滤器的作用
  • 能够编写过滤器
  • 能够说出过滤器生命周期相关方法
  • 能够根据过滤路径判断指定的过滤器是否起作用
  • 能够说出什么是过滤器链
  • 能够编写过滤器解决全局乱码
  • 能够说出监听器的作用
  • 能够使用ServletContextListener监听器

01_JavaWeb 三大组件概述-[★★]

  • JavaWeb三大组件
JavaWeb三大组件作用实现接口
Servlet小程序:用来处理用户请求并相应数据javax.servlet.Servlet
Filter过滤器:用来拦截用户请求和响应javax.servlet.Filter
Listener监听器:监听web项目运行过程产生的事件。javax.servlet.XxxListener

02_过滤器概述和应用场景-[★★]

过滤器的作用:
  拦截用户请求:可以在请求达到目标资源之前对请求进行处理,然后根据需求决定放行请求(将请求传递给目标资源)
  拦截服务器响应:可以对响应数据进行处理,然后将处理后的结果返回给浏览器。

03_过滤器入门案例-[★★★]

过滤器开发步骤小结

  1. 创建类实现Filter接口
  2. 重写接口的所有抽象方法
  3. 在doFilter方法中拦截请求和响应
  4. 配置过滤器的过滤路径:xml配置或注解配置
  5. 部署项目
  • 需求:创建一个过滤器HelloFilter,在运行HelloServlet(Web资源)前和后分别输出一句话,在HelloServlet中也输出一句话,观察控制台的运行效果。

  • HellServlet代码

@WebServlet(urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("Web资源:到达了Servlet中");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}
  • HelloFilter代码
/**
 * 目标:掌握过滤器的开发步骤

   需求:创建一个过滤器HelloFilter,在运行HelloServlet(Web资源)前和后分别输出一句话,
       在HelloServlet中也输出一句话,观察控制台的运行效果。

   开发步骤
        1. 创建一个类实现Filter接口
        2. 重写接口中的所有方法
        3. 在doFilter方法中拦截请求和响应
        4. 配置过滤器的过滤路径:指定对哪些资源进行过滤
            4.1 可以通过web.xml配置
            4.2 可以通过注解配置
        5. 部署项目并通过浏览器访问目标资源测试
 */
// 1. 创建一个类实现Filter接口
@WebFilter(urlPatterns = "/hello")
public class HelloFilter implements Filter {

    // 2. 重写接口中的所有方法
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    // 3. 在doFilter方法中拦截请求和响应
    @Override
    public void doFilter(ServletRequest servletRequest,
                         ServletResponse servletResponse,
                         FilterChain filterChain) throws IOException, ServletException {
        System.out.println("过滤器:我是请求到达的时候执行的");
        // 放行请求:将请求传递给下一个资源(Servlet)
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("过滤器:我是响应回来的时候执行的");
    }

    @Override
    public void destroy() {

    }
}
  1. 使用配置文件配置
<?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">

    <!--配置过滤器信息-->
    <filter>
        <!--过滤器名字-->
        <filter-name>hello</filter-name>
        <!--过滤器的类全名字符串-->
        <filter-class>com.pkx._01filter._01filter_demo.HelloFilter</filter-class>
    </filter>

    <filter-mapping>
        <!--过滤器名字:必须和上面的一种-->
        <filter-name>hello</filter-name>
        <!--过滤器的过滤路径:必须以/开头-->
        <url-pattern>/hello</url-pattern>
    </filter-mapping>

</web-app>
  1. 使用注解方式配置
@WebFilter(urlPatterns = "/hello")

04_过滤器的执行流程-[★★]

  • 执行流程图
    在这里插入图片描述
  1. 浏览器请求web资源:可以是静态资源(HTML/JS/CSS/图片),也可以是动态资源(JSP、Servlet)
  2. 当过滤器的过滤路径和web资源的访问路径相匹配时,则请求会先经过过滤器处理
  3. 由过滤器决定是否将请求传递给下一个资源,如果要传递给下一个资源则要调用过滤器链的doFilter方法
  4. Web资源响应数据到浏览器之前,也会经过过滤器处理:将处理后的结果响应给浏览器显示。

05_过滤器的生命周期-[★★★]

  • Filter接口中的方法
Filter接口中的方法作用和调用次数
void init(FilterConfig filterConfig)执行初始化操作 执行1次
void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
过滤请求和响应
每次拦截到请求都会执行1次
public void destroy()销毁资源 执行1次
  • 示例代码
/**
 *  目标:过滤器的生命周期

    过滤器的创建时间:在服务器启动时创建
    过滤器的销毁时间:在服务器关闭或重启

    注意:过滤器是不能直接被用户访问的。
 */
@WebFilter(urlPatterns = "/life")
public class LifeFilter implements Filter {

    // 0 无参数构造方法  执行1次
    public LifeFilter(){
        System.out.println("LifeFilter构造方法");
    }

    // 1. 初始化方法:执行初始化操作  执行1次
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("init...");
    }

    // 2. 过滤方法:拦截请求和响应  每拦截到请求都会执行1次
    @Override
    public void doFilter(ServletRequest servletRequest,
                         ServletResponse servletResponse,
                         FilterChain filterChain) throws IOException, ServletException {
        System.out.println("doFilter...");
        // 放行请求:将请求传递给下一个资源(Servlet)
        filterChain.doFilter(servletRequest,servletResponse);
    }

    // 3. 销毁方法:在服务器关闭或重启执行,用来释放资源  执行1次
    @Override
    public void destroy() {
        System.out.println("destroy...");
    }
}

06_过滤器的映射路径-[★★★]

过滤器映射路径的配置方式
 1. 精确路径匹配
  特点:过滤的路径和要访问的资源路径完全相同
  须知:过滤器可以过滤任何资源:
  servlet,index.jsp,index.html,a.png
 2. 模糊路径匹配:使用通配符*,* 代表匹配所有资源
  1. 前缀匹配:过滤路径必须以/开头,以结尾
  比如:/
过滤当前项目下所有资源
  /manager/* 过滤manager目录下的所有资源
  2. 后缀匹配:过滤路径必须以开头,以扩展名结尾
  比如:
.do 过滤所有以.do结尾的资源
  .action 过滤所有以.action结尾的资源
  3. 错误写法:/
.do
  不能同时出现/开头和扩展名结尾配置方式,
  导致整个web项目加载失败,所有资源都无法访问。

以/ 开关的匹配模式和以扩展名结尾的配置不能同时出现:比如:/manager/*.html

  • 示例代码
/**
    目标:掌握过滤器映射路径的两种配置方式

    两种配置方式
        1. 精确路径配置
            * 过滤器的过滤地址和目标资源的访问地址必须完全一致
            * 比如:过滤器的过滤地址是:/url  则目标资源的地址必须也是/url才会触发该过滤器的执行

        2. 模糊路径配置:使用通配符 * : 代表任意路径
            2.1 前缀路径配置
                * 格式: 必须以/开头,以* 结尾
                * 比如: /* :该过滤器可以过滤项目中的所有资源。
                * 比如: /manager/*:该过滤器要过滤manager目录下所有资源

            2.2 后缀路径配置
                * 格式:必须以*开头,以扩展名结尾
                * 比如:*.do 或 *.action :过滤以do或action结尾的资源

        3. 注意事项
             错误写法:/*.do
             不能同时出现/开头和扩展名结尾配置方式,
             导致整个web项目加载失败,所有资源都无法访问。
             Invalid <url-pattern> /*.do in filter mapping
 */
@WebFilter(urlPatterns = "*.do")
public class URLPatternFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("URLPatternFilter...doFilter进来了吗....");
        // 放行
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }
}

07_过滤器默认拦截方式-[★★★]

过滤器默认的拦截方式

  1. 只对从浏览器直接发送过来的请求进行拦截
  • 需求说明:
    1.创建过滤器:DispatchTypeFilter,过滤路径:/two
    2.创建 OneServlet,访问路径为:/one,在 OneServlet 中重定向到TwoServlet
    3.创建 TwoServlet,访问路径为:/two

  • DispatchTypeFilter 代码

/**
     目标:学习过滤器默认的拦截方式

     默认拦截方式:只对浏览器发出的请求进行拦截(包括重定向)

     需求说明:
     - 创建过滤器:DispatchTypeFilter,过滤路径:/two
     - 创建OneServlet,访问路径为:/one,在OneServlet中重定向到TwoServlet
     - 创建TwoServlet,访问路径为:/two

     问题1:浏览器直接访问OneServlet,会触发当前过滤器执行吗?会触发
     问题2:浏览器直接访问TwoServlet,会触发当前过滤器执行吗?会触发

 */
@WebFilter(urlPatterns = "/two")
public class DispatchTypeFilter implements Filter {
    public void destroy() {

    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("DispatchTypeFilter...");
        chain.doFilter(req, resp);
    }

    public void init(FilterConfig config) throws ServletException {

    }

}
  • OneServlet 代码
// 需求:在OneServlet中重定向到TwoServlet
@WebServlet(urlPatterns = "/one")
public class OneServlet extends HttpServlet {

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 在OneServlet中重定向到TwoServlet
        response.sendRedirect("two");

        // 使用转发跳转到TwoServlet
        // request.getRequestDispatcher("two").forward(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}
  • TwoServlet 代码
@WebServlet(urlPatterns = "/two")
public class TwoServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
         // 设置内容类型和编码
         response.setContentType("text/html;charset=utf-8");
         // 获得字符打印流
         PrintWriter out = response.getWriter();
         out.println("我是TwoServlet的响应");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

访问方式如下

  • 浏览器直接访问OneServlet,观察是否执行了过滤器?会
  • 浏览器直接访问TwoServlet,观察是否执行了过滤器?会

08_修改过滤器拦截方式-[★★★]

  • 需求说明:
    1.创建过滤器:ForwardRequestFilter,过滤路径:/second
    2.创建 FirstServlet,访问路径为:/first,在 FirstServlet 中转发到SecondServlet
    3.创建 SecondServlet,访问路径为:/second

  • ForwardRequestFilter 代码

/**
  目标:修改默认的拦截方式并实现多个拦截方式共用

  问题1:直接访问SecondServlet会执行该过滤器吗?会执行
  问题2:直接访问FirstServlet会执行该过滤器吗?

  修改过滤器拦截方式
    1. 通过web.xml修改
    2. 通过注解dispatcherTypes属性修改
        * DispatcherType.REQUEST:默认值:只对浏览器发送的请求拦截
        * DispatcherType.FORWARD:对转发的请求进行拦截
 */
@WebFilter(urlPatterns = "/second",
        dispatcherTypes = {
                DispatcherType.FORWARD, // 对转发的请求进行拦截
                DispatcherType.REQUEST,// 默认值:只对浏览器发送的请求拦截
        })
public class ForwardRequestFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("ForwardRequestFilter....");
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }
}
  • FirstServlet 代码
// 需求:在FirstServlet中转发到SecondServlet
@WebServlet(urlPatterns = "/first")
public class FirstServlet extends HttpServlet {

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 在OneServlet中重定向到TwoServlet
        // response.sendRedirect("two");

        // 使用转发跳转到SecondServlet
        request.getRequestDispatcher("second").forward(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

  • SecondServlet 代码
@WebServlet(urlPatterns = "/second")
public class SecondServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
         // 设置内容类型和编码
         response.setContentType("text/html;charset=utf-8");
         // 获得字符打印流
         PrintWriter out = response.getWriter();
         out.println("我是SecondServlet的响应");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

修改默认拦截方式

拦截方式说明
REQUEST默认值:只对浏览器发送的请求拦截(包括重定向)
FORWARD对转发的请求进行拦截
  • 在配置文件中修改:web.xml
<filter>
    <filter-name>forward</filter-name>
    <filter-class>com.pkx._01filter._03filter_type.ForwardRequestFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>forward</filter-name>
    <url-pattern>/second</url-pattern>
    <!--配置拦截方式-->
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
</filter-mapping>
  • 在注解中修改:@WebFilter
@WebFilter(urlPatterns = "/second",
        dispatcherTypes = {
                DispatcherType.FORWARD, // 对转发的请求进行拦截
                DispatcherType.REQUEST,// 默认值:只对浏览器发送的请求拦截
        })

09_过滤器链概述和演示-[★★★]

什么是过滤器链:由多个过滤器连接一起组成的链条

  • 需求:创建两个过滤器OneFilter和TwoFilter,访问ResourceServlet,每个过滤器的请求和响应各输出一句话,观察过滤器的执行过程。

  • 第一个过滤器:OneFilter

/**
 需求:创建两个过滤器OneFilter和TwoFilter,
        访问ResourceServlet,每个过滤器的请求和响应各输出一句话,观察过滤器的执行过程。
 */
@WebFilter(urlPatterns = "/resource")
public class OneFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("过滤器1:请求");
        // 放行请求:将请求传递下去
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("过滤器1:响应");
    }

    @Override
    public void destroy() {

    }
}
  • 第二个过滤器:TwoFilter
@WebFilter(urlPatterns = "/resource")
public class TwoFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("过滤器2:请求");
        // 放行请求
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("过滤器2:响应");
    }

    @Override
    public void destroy() {

    }
}
  • Web资源:ResourceServelt
@WebServlet(urlPatterns = "/resource")
public class ResourceServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("我是web资源Resource");
    }
}

10_过滤器链的执行顺序-[★★★]

  1. 过滤器链的执行顺序由什么决定?
    web.xml配置方式:将需要先执行的过滤器配置在前面
    注解配置方式:只需要修改名称即可,不同系统效果不一样(如果需要严格控制过滤器链过滤器的执行顺序不推荐使用注解配置)
  2. 过滤器链的注意事项
    如果过滤器执行完过滤操作之后不放行请求,下一个过滤器无法拦截到请求。
  • 修改过滤器链的执行顺序
  1. 注解配置方式:只需要修改名称即可
  2. web.xml配置方式:将需要先执行的过滤器配置在前面

11_过滤器案例01:解决全局乱码-[★★★★]

  • 需求说明:编写过滤器,过滤所有Servlet中使用POST方法提交的汉字的编码。

  • 步骤分析:
    1.创建login.jsp页面:准备登录表单,表单提交给LoginServlet
    2.创建register.jsp页面:准备注册表单,表单提交给RegisterServlet
    3.创建LoginServlet和RegisterServlet接收表单参数信息
    4.创建EncodeFilter过滤LoginServlet和RegisterServlet解决请求参数乱码问题

  • login.jsp代码

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
      <title>登录页面</title>
</head>
  <body>
      <form action="login" method="post">
        用户名:<input type="text" name="username"><br>
        密码:<input type="password" name="password"><br>
        <input type="submit" value="登录">
    </form>
  </body>
</html>
  • register.jsp代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>注册页面</title>
  </head>
  <body>
      <form action="register" method="post">
          用户名:<input type="text" name="username"><br>
          密码:<input type="password" name="password"><br>
          <input type="submit" value="注册">
      </form>
  </body>
</html>
  • LoginServlet代码
 @WebServlet(urlPatterns = "/login")
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 获取请求参数:用户名
        String username = request.getParameter("username");
        System.out.println("username = " + username);
    }
}
  • RegisterSerlvet代码
@WebServlet(urlPatterns = "/register")
public class RegisterServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 获取请求参数:用户名
        String username = request.getParameter("username");
        System.out.println("username = " + username);
    }
}
  • EncodeFilter代码
@WebFilter(urlPatterns = "/*")
public class EncodingFilter implements Filter {
  
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // 将父类类型转换子类类型的对象
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        // 获取请求方式
        String method = request.getMethod();
        // 判断请求方式是GET还是POST
        if ("GET".equals(method)){
            // 处理GET请求乱码问题

        } else {
            // 设置请求参数乱码:POST请求乱码处理方式
            servletRequest.setCharacterEncoding("utf-8");
        }
        // 放行请求
        filterChain.doFilter(servletRequest, servletResponse);
    }
  
    @Override
    public void destroy() {
  
    }
}

12_过滤器案例02:用户权限的控制-[★★★★]

当多个资源需要执行相同的处理逻辑时,可以统一使用过滤器实现该处理逻辑,简化代码。

  • 案例需求
    使用过滤器进行权限的控制,实现正确的访问
    在这里插入图片描述

add.jsp 添加数据,需要登录才可访问
update.jsp 修改数据,需要登录才可访问
list.jsp 查询数据,不用登录
login.jsp 登录页面

  • 实现步骤
  1. 在web目录下准备4个页面:add.jsp、update.jsp、list.jsp、login.jsp
  2. 创建LoginServlet处理登录逻辑
    * 判断用户名密码是否正确,如果正确,则在会话域中保存用户信息。登录成功跳转到list.jsp
    * 登录失败则在域中写入登录失败的信息,并且跳转到login.jsp
  3. 使用过滤器解决:创建AuthorityFilter
    * 获得HttpServletRequest、HttpSession对象
    * 如果会话域中没有用户信息,则跳转到登录页面:login.jsp
    * 如果会话域中有用户信息,则放行运行访问资源。
  • 用户权限分析图

在这里插入图片描述

  • LoginServlet代码
@WebServlet(urlPatterns = "/login")
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. 获取用户名和密码
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        // 2. 判断用户名和密码是否正确
        if ("admin".equals(username) && "123".equals(password)){
            // 3. 如果正确则保存用户信息到会话域中
            request.getSession().setAttribute("user", username);
            // 3.1 跳转到主界面
            response.sendRedirect("list.jsp");
        } else {
            // 4. 如果不正确则保存错误信息到请求域中
            request.setAttribute("errorMsg", "用户名或密码错误");
            // 4.1 转发的登录页面
            request.getRequestDispatcher("login.jsp").forward(request, response);
        }
    }
}
  • AuthorFilter代码
/**
 * 目标:拦截manager目录下资源的访问,需要登录才能访问
 */
@WebFilter(urlPatterns = "/manager/*")
public class AuthorFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // 1. 获取会话域对象
        // 1.1 类型转换
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        // 1.2 获取会话域对象
        HttpSession session = request.getSession();
        // 2. 从会话域中获取用户数据
        Object user = session.getAttribute("user");
        // 3. 有用户数据则代表已经登录了
        if (user != null) {
            // 则放行请求
            filterChain.doFilter(request, response);
        } else {
            // 4. 没有用户数据则代表没有登录,则重定向到登录页面
            response.sendRedirect(request.getContextPath() + "/login.jsp");
        }

    }

    @Override
    public void destroy() {

    }
}

13_监听器概述-[★★]

  1. 监听器的作用:监听作用域的创建和销毁,以及属性增删改查操作。
  2. 监听器的开发步骤:
    1. 创建类实现监听器接口
    2. 重写接口中的所有抽象方法
    3. 配置监听器(web.xml或注解配置)
  • 监听器的分类
监听器接口作用
ServletContextListener监听上下文域的创建和销毁
ServletContextAttributeListener监听上下文域属性的增删改查操作
HttpSessionListener监听会话域的创建和销毁
HttpSessionAttributeListener监听会话域属性的增删改查操作
ServletRequestListener监听请求域的创建和销毁
ServletRequestAttributeListener监听请求域属性的增删改查操作

14_ServletContextAttributeListener监听器演示-[★★★]

  • ServletContextAttributeListener接口中方法
ServletContextAttributeListener接口中方法调用时机
void attributeAdded(ServletContextAttributeEvent event)往上下文域添加键值对数据调用
void attributeRemoved(ServletContextAttributeEvent event)从上下文域删除键值对数据调用
void attributeReplaced(ServletContextAttributeEvent event)修改上下文域键值对数据调用
  • ServletContextAttributeEvent对象中的方法
ServletContextAttributeEvent对象中的方法功能
String getName()获得属性名
Object getValue()获得属性值
  • 需求
    1.创建一个ServletContextAttributeListener监听器的实现类
    重写接口中所有的方法,输出属性名和属性值。
    2.创建一个Servlet,向context上下文中添加一个属性,修改一个属性,删除一个属性。

  • Servlet代码

/**
 * 需求:向context上下文中添加一个属性,修改一个属性,删除一个属性。
 */
@WebServlet(urlPatterns = "/context")
public class ServletContextServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 获得上下文域对象
        ServletContext context = getServletContext();
        // 添加键值对数据
        context.setAttribute("username", "jack");
        // 更新键值对数据
        context.setAttribute("username", "rose");
        // 删除键值对数据
        context.removeAttribute("username");
    }
}
  • 监听器代码
/**
 需求:
 1. 创建一个ServletContextAttributeListener监听器的实现类
    - 重写接口中所有的方法,输出属性名和属性值。
 2. 创建一个Servlet,向context上下文中添加一个属性,修改一个属性,删除一个属性。
 3. 控制台输出效果:
 */
public class MyServletContextAttributeListener implements ServletContextAttributeListener {

    // 当往上下文域添加属性时调用
    @Override
    public void attributeAdded(ServletContextAttributeEvent event) {
        // 获得属性名
        String name = event.getName();
        // 获得属性值
        Object value = event.getValue();
        System.out.println("attributeAdded:" + name + "=" + value);
    }

    // 当从上下文域中删除属性时调用
    @Override
    public void attributeRemoved(ServletContextAttributeEvent event) {
        // 获得属性名
        String name = event.getName();
        // 获得属性值
        Object value = event.getValue();
        System.out.println("attributeRemoved:" + name + "=" + value);
    }

    // 当更新上下文域中属性时调用
    @Override
    public void attributeReplaced(ServletContextAttributeEvent event) {
        // 获得属性名
        String name = event.getName();
        // 获得属性值:修改前的值
        Object value = event.getValue();
        // 获得属性值:修改后的值
        Object newValue = event.getServletContext().getAttribute(name);
        System.out.println("attributeReplaced:" + name + "=" + value + "," + newValue);
    }
}

15_ServletContextListener应用案例-[★★★]

  • ServletContextListener常见操作
    1.加载第三方配置文件
    2.开启定时任务(比如每天凌晨给过当天生日的用户发生日祝福邮件)

  • 需求说明:在项目启动之后,开启一个定时任务,每隔3秒在控制台输出一句话。

/**
 * 需求说明:在项目启动之后,开启一个定时任务,每隔3秒在控制台输出一句话。
 */
@WebListener
public class MyServletContextListener implements ServletContextListener {

    // 在服务器启动时执行(上下文域创建)
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("contextInitialized");

        // 开启一个定时任务,每隔3秒在控制台输出一句话。
        // 创建定时器对象
        Timer timer = new Timer();
        // 安排定时任务
        /**
         * 参数1:task:TimerTask对象:用来封装任务代码
                 等价于Runnable接口:用来封装线程任务代码
         * 参数2:firstTime:任务第1次开始执行的时间
         * 参数3:period:时间间隔,单位毫秒
         */
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("定时任务...");
            }
        },new Date(),3000);
    }

    // 在服务器关闭时执行(上下文域销毁)
    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("contextDestroyed");
    }
}

16_补充内容- junit

  1. 什么是junit
    junit是第三方提供的单元测试框架(工具)

    什么是单元测试
    * 在Java中一个类就是一个单元
    * 单元测试:程序猿编写的一小段代码对某个类中的某个方法进行测试,最大限度避免bug
    保证方法执行的正确性和稳定性。

  2. junit使用步骤
    2.1 编写业务类:实现某一业务功能的类,比如对学生进行增删改查的类。
    编写业务方法:实现某一功能的方法,比如登录,查询学生,注册…

    2.2 编写测试类
    命名规范:
    * 以Test开头,以业务类名结尾,比如业务类名:StudentDao,则测试类名:TestStudentDao
    * 以Test结尾,以业务类名开头,比如业务类名:StudentDao,则测试类名:StudentDaoTest

    2.3 编写测方式:在测试类中编写测试方法
    命名规范:
    * 一般以test开头,以业务方法名结尾,比如业务方法名:save,则测试方法名:testSave
    声明要求:必须是public修饰的,必须是没有返回值,必须是没有参数,必须使用@Test修饰

  3. junit使用演示

  4. junit常用注解
    junit4.x版本
    @Before:用来修饰方法,该方法会在每个测试方法执行之前执行1次
    @After:用来修饰方法,该方法会在每个测试方法执行之后执行1次
    @BeforeClass:用来修饰静态方法,该方法会在所有测试方法执行之前执行1次
    @AfterClass:用来修饰静态方法,该方法会在所有测试方法执行之后执行1次

    junit5.x版本
    @BeforeEach:用来修饰方法,该方法会在每个测试方法执行之前执行1次
    @AfterEach:用来修饰方法,该方法会在每个测试方法执行之后执行1次
    @BeforeAll:用来修饰静态方法,该方法会在所有测试方法执行之前执行1次
    @AfterAll:用来修饰静态方法,该方法会在所有测试方法执行之后执行1次

// 测试类
public class TestStudentDao {

    // 创建数据访问层对象
    private static StudentDao dao = null;


    // 用来修饰方法,该方法会在每个测试方法执行之前执行1次
    /*@Before
    public void init(){
        dao = new StudentDao();
    }*/

    // 用来修饰静态方法,该方法会在所有测试方法执行之前执行1次
    @BeforeAll
    public static void init(){
        System.out.println("init");
        dao = new StudentDao();
    }
    // 用来修饰方法,该方法会在每个测试方法执行之后执行1次
    /*@After
    public void destory(){
        dao = null;
    }*/


    // 用来修饰静态方法,该方法会在所有测试方法执行之后执行1次
    @AfterAll
    public  static  void destory(){
        System.out.println("destory");
        dao = null;
    }

    /**
     * 测试方法
     */
    @Test
    public void testSaveStudent(){
        // 创建学生对象
        Student stu = new Student();
        // 调用保存方法保存学生
        boolean b = dao.saveStudent(stu);
        /*
         断言:预先判断某个条件一定满足,如果条件不满足则直接崩溃
         message:  异常提示信息
         expected: 期望值:调用方法期望获得的返回值
         actual: 实际值:调用方法返回的实际结果
         */
        // Assert.assertEquals("期望值和实际值不一致", true,b);
        System.out.println("b = " + b);
    }

    @Test
    public void testDeleteById(){
        // 创建数据访问层对象
        // StudentDao dao = new StudentDao();
        // 根据id删除学生
        dao.deleteById(1);
    }
}

总结

过滤器

  • 作用:拦截用户有请求和服务器响应
  • 步骤:
    1.创建类实现Filter接口
    2.重写接口的所有抽象方法
    3.在doFilter方法中拦截请求和响应
    4.配置过滤器的过滤路径:xml配置或注解配置
    5.部署项目
  • Filter接口:
    void init(FilterConfig filterConfig)
    void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    public void destroy()

监听器

  • 作用:监听作用域的创建和销毁,以及属性增删改查操作。
  • 监听器接口:
    ServletContextListener
    ServletContextAttributeListener
    HttpSessionListener
    HttpSessionAttributeListener
    ServletRequestListener
    ServletRequestAttributeListener
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值