Web组件:Servlet & Listener & Filter

1 前言

1.1 内容概要

  1. 掌握ServletContextListener的使用,并且理解其执行时机
  2. 掌握Filter的使用,并且理解其执行时机
  3. 能够使用Filter解决一些实际的问题

1.2 前置知识准备

  • Servlet的执行

  • ServletContext的功能和使用

2 Web组件

JavaEE的三大Web组件

  1. Servlet → 处理请求对应的业务
  2. Listener → 监听器
  3. Filter → 过滤器

2.1 Listener监听器

顾名思义就是监听东西的,其实和命名有关系,我们提供的是什么监听器就是监听什么的。

UserListener 就是监听User

监听器在监听到主体做了XX事情,就会触发对应的事件。

2.2 ServletContextListener

监听的主体就是ServletContext,当发现ServletContext做了事情,监听器就会执行该事件特定的方法

  • ServletContext如果初始化,则会执行监听器的初始化方法
  • ServletContext如果销毁,则会执行监听器的销毁方法
  1. 如果我们想要在应用程序启动的过程中,实现一些自定义的代码,需要方法ServletContextListener的监听ServletContext初始化的方法对应的执行方法 → 监听器的初始化方法,会在应用程序启动的时候执行,主要做一些资源的初始化
  2. 如果我们想要在应用程序关闭的过程中,实现一些自定义的代码,放在监听器的销毁方法 → 监听器的销毁方法,会在应用程序关闭(卸载的时候执行) ,主要做资源的释放

2.3 执行过程

当应用程序启动的过程中,逐步加载Web组件

  • 首先会加载ServletContext和Listener组件
    • ServletContext伴随着应用程序初始化,它开始初始化,然后ServletContextListener监听到ServletContext初始化,会执行Listener的Initialized方法
  • 然后初始化loadOnStartup为正数的Servlet

改造之前的业务代码,之前整合MyBatis时,SqlSessionFactory的初始化是通过Servlet的生命周期init方法,当前可以通过ServletContextListener,在应用程序启动的时候,执行contextInitialized方法,在该方法中进行SqlSessionFactory初始化过程,并将其放到ServletContext中

java 复制代码
@WebListener
public class CustomServletContextListener implements ServletContextListener {
    // 当ServletContext初始化的时候执行
    // 应用程序启动的时候向ServletContext中塞入一些数据
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        ServletContext servletContext = servletContextEvent.getServletContext();
        SqlSessionFactory sqlSessionFactory = null;
        try {
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis.xml"));
            System.out.println("ServletContext初始化");
            System.out.println("sqlSessionFactory = " + sqlSessionFactory);
        } catch (IOException e) {
            e.printStackTrace();
        }
        servletContext.setAttribute("SqlSessionFactory",sqlSessionFactory);
    }

    // 当ServletContext销毁的时候执行
    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("ServletContext销毁");

    }
}
java 复制代码
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {

    SqlSessionFactory sqlSessionFactory;

    @Override
    public void init() throws ServletException {
        ServletContext servletContext = getServletContext();
        sqlSessionFactory = (SqlSessionFactory) servletContext.getAttribute("SqlSessionFactory");
        System.out.println("Servlet初始化");
        System.out.println("sqlSessionFactory = " + sqlSessionFactory);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    }

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

提供Listener,其实主要就是去初始化这个ServletContext

SpringMVC就是基于这样的特点去实现的

3 Filter

Filter是一个执行过滤任务的一个对象。它既可以作用于Request对象,也可以作用于Response对象,或者两者均作用。

也就是Servlet中获取请求之前,Servlet响应之后

3.1 Filter和Servlet的执行

URL-Pattern和Servlet之间存在着映射关系,URL-Pattern和Filter之间也存在着映射关系。

  • 1个URL-Pattern只能对应一个Servlet,但是可以对应多个Filter
  • Servlet和URL-Pattern之间是一对多的关系,但是URL-Pattern和Servlet之间是一对一

其实就意味着一件事,当我们发起一个请求的时候,其实就是一个URL-Pattern对应的请求

  • 对应1个Servlet
  • 对应多个Filter

如果只有一个过滤器那么执行流程如下

多个过滤器,就是就组成了一个过滤器的链,依次执行过滤器

如果增加上对应的方法

有一个问题,是否每一次都会继续执行到下一个拦截器或Servlet?不一定,去界定是否为放行状态

doFilter这个方法中,提供了一个形参,形参叫filterChain,filterChain中提供了一个doFilter方法,如果执行这个方法就是放行,如果不执行,则中断流程

3.2 使用

java 复制代码
/**
 * localhost:8080/demo5/hello
 * localhost:8080/demo5/bye
 * URL-Pattern对于上面两个请求都能起作用,那么我们的URL-Pattern可以设置为 /*
 */
@WebFilter("/*")
public class URLPrintFilter 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 url = request.getRequestURL().toString();
        System.out.println("url = " + url);
        System.out.println("Filter的前半部分");
        //放行
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("Filter的后半部分");
    }

    @Override
    public void destroy() {

    }
}

就算没有Servlet,仍然是可以执行到Filter的


Filter能否继续执行,取决于FilterChain的doFilter方法是否执行

3.3 案例

3.3.1 给请求和响应设置字符集

Post请求中文乱码

request.setCharacterEncoding("utf-8")

响应的时候,响应的字符中文乱码

response.setContentType("text/html;charset=utf-8")

java 复制代码
@WebFilter("/*")
public class CharacterEncodingFilter 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;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        request.setCharacterEncoding("utf-8");
        //response.setContentType("text/html;charset=utf-8");
        response.setContentType("application/json;charset=utf-8");
        filterChain.doFilter(request, response);
    }

    @Override
    public void destroy() {

    }
}

3.3.2 登录案例

Http://localhost:8080/demo6/user/login

Http://localhost:8080/demo6/user/info

在Session中是否有存储用户的信息

  1. login登录实现的什么事情?
    1. 验证用户传入的用户名和密码,和数据库中存储的信息是否一致
    2. 验证如果成功,将用户信息存到Session里面
  2. info查看用户信息实现的是什么事情
    1. 从Session中取出信息,分析是否取出了信息
    2. 如果取出了信息,说明登录成功了,根据取出的信息查询用户的具体信息
    3. 如果没有取出信息,提示未登录的json数据
  3. order/list 查询当前用户的订单信息
    1. 从Session中取出信息,分析是否取出了信息
    2. 如果取出了信息,说明登录成功了,根据取出的信息查询用户的订单信息
    3. 如果没有取出信息,提示未登录的json数据
  4. 发现了2a和3a做的是相同的事情,2c和3c做的也实现相同的事情,相同的事情我们可以提取到Filter中
  5. 如果我提取到了Filter中 2中只有2b ,3中只有3b

4 小结

4.1 Web组件

  • 核心是Servlet,处理核心业务
  • Listener,用来做资源的初始化
  • Filter,在Servlet处理前后增加通用的处理
相关推荐
恋猫de小郭38 分钟前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅8 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅9 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊9 小时前
jwt介绍
前端