Web组件

Web组件概述

JavaEE定义了三大Web组件,它们共同构成了Web应用的核心处理机制:

组件职责执行时机
Servlet处理请求对应的业务逻辑接收到请求时
Listener监听Web应用生命周期事件应用启动/关闭时
Filter对请求/响应进行过滤处理Servlet前后

三者的执行顺序:Listener → Filter → Servlet → Filter

 Listener监听器

什么是监听器

监听器(Listener)是JavaEE提供的事件监听机制。监听器会监听特定的主体(如ServletContext、HttpSession、ServletRequest等),当主体发生特定事件(初始化、销毁、属性变化等)时,自动触发对应的方法。

核心思想:通过监听机制,在关键生命周期节点自动执行初始化或清理操作。

ServletContextListener

ServletContextListener是最常用的监听器,用于监听ServletContext的生命周期:

事件触发时机对应方法
初始化应用程序启动时contextInitialized()
销毁应用程序关闭时contextDestroyed()

生命周期说明

应用程序启动
    ↓
ServletContext初始化
    ↓
触发contextInitialized()  ← 在这里进行资源初始化
    ↓
应用运行中...
    ↓
应用程序关闭
    ↓
ServletContext销毁
    ↓
触发contextDestroyed()   ← 在这里进行资源释放

执行过程图解

当应用程序启动时,Web组件按以下顺序加载:

  1. 首先加载:ServletContext 和 Listener
  2. 然后加载:loadOnStartup ≥ 0 的Servlet
应用启动
   ├── 创建ServletContext
   ├── 执行ServletContextListener.contextInitialized()
   │       └── 初始化全局资源(如数据库连接池、SqlSessionFactory)
   ├── 初始化loadOnStartup ≥ 0的Servlet
   └── 应用就绪,等待请求

配置方式

方式一:注解配置(推荐)

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

@WebListener  // 无需指定value,自动监听
public class CustomServletContextListener implements ServletContextListener {
    
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        // 应用启动时执行
        System.out.println("✅ 应用程序启动,ServletContext初始化完成");
    }
    
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        // 应用关闭时执行
        System.out.println("❌ 应用程序关闭,ServletContext销毁");
    }
}

方式二: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">
    
    <!-- 监听器配置 -->
    <listener>
        <listener-class>com.example.listener.CustomServletContextListener</listener-class>
    </listener>
    
</web-app>

两种方式等效,但注解配置更简洁,是Servlet 3.0+的推荐方式。

MyBatis集成优化

场景描述

之前整合MyBatis时,SqlSessionFactory在Servlet的init()方法中初始化。更好的做法是在应用启动时就完成初始化,并存入ServletContext供全局使用。

完整代码实现

监听器代码

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import java.io.IOException;
import java.io.InputStream;

@WebListener
public class MyBatisInitListener implements ServletContextListener {
    
    private static final String CONFIG_FILE = "mybatis-config.xml";
    private static final String CONTEXT_KEY = "SqlSessionFactory";
    
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        ServletContext context = sce.getServletContext();
        
        try (InputStream is = Resources.getResourceAsStream(CONFIG_FILE)) {
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
            
            // 存入ServletContext,全局共享
            context.setAttribute(CONTEXT_KEY, factory);
            
            System.out.println("✅ MyBatis初始化成功,SqlSessionFactory已存入ServletContext");
        } catch (IOException e) {
            System.err.println("❌ MyBatis初始化失败: " + e.getMessage());
            throw new RuntimeException("MyBatis初始化失败", e);
        }
    }
    
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        // 清理资源
        ServletContext context = sce.getServletContext();
        context.removeAttribute(CONTEXT_KEY);
        System.out.println("✅ MyBatis资源已清理");
    }
}

Servlet中使用

import org.apache.ibatis.session.SqlSessionFactory;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/user/list")
public class UserListServlet extends HttpServlet {
    
    private SqlSessionFactory sqlSessionFactory;
    
    @Override
    public void init() throws ServletException {
        // 从ServletContext获取已初始化的工厂
        sqlSessionFactory = (SqlSessionFactory) getServletContext()
                .getAttribute("SqlSessionFactory");
        
        if (sqlSessionFactory == null) {
            throw new ServletException("SqlSessionFactory未初始化");
        }
    }
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        // 使用sqlSessionFactory执行业务操作
        // ...
    }
}

其他常用监听器

监听器接口监听对象用途
HttpSessionListenerSession统计在线人数、Session超时处理
ServletRequestListenerRequest请求日志记录、性能监控
HttpSessionAttributeListenerSession属性监听登录/登出状态变化

Filter过滤器

什么是过滤器

Filter(过滤器)是运行在Servlet之前的组件,可以对请求进行预处理,对响应进行后处理。

执行位置

客户端请求
    ↓
Filter预处理(请求过滤)
    ↓
Servlet业务处理
    ↓
Filter后处理(响应过滤)
    ↓
返回客户端

Filter和Servlet的映射关系

对比项ServletFilter
URL映射1个URL → 1个Servlet1个URL → 多个Filter
执行顺序按URL匹配按配置顺序
终止性处理请求可选择放行或拦截

 重要:一个请求可以经过多个Filter,形成过滤器链(FilterChain)

过滤器链执行流程

单个Filter执行流程

请求 → Filter.doFilter()前半部分 
          ↓
      chain.doFilter()  ← 放行
          ↓
      Servlet处理
          ↓
      Filter.doFilter()后半部分 → 响应

多个Filter执行流程

假设配置了FilterA、FilterB、FilterC三个过滤器:

请求 → FilterA → FilterB → FilterC → Servlet
                                      ↓
响应 ← FilterA ← FilterB ← FilterC ← 处理完成

执行顺序遵循先进后出(类似栈结构):

FilterA开始
  FilterB开始
    FilterC开始
      Servlet处理
    FilterC结束
  FilterB结束
FilterA结束

关键方法说明

// 放行方法:必须调用才能继续后续流程
filterChain.doFilter(request, response);

如果不调用chain.doFilter(),请求将被拦截,不会到达Servlet。

Filter生命周期

阶段方法执行次数用途
初始化init(FilterConfig)1次读取配置参数
过滤doFilter()每次请求执行过滤逻辑
销毁destroy()1次释放资源

配置方式

方式一:注解配置(推荐)

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter("/*")  // 过滤所有请求
public class LoggingFilter implements Filter {
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 初始化操作
        String encoding = filterConfig.getInitParameter("encoding");
        System.out.println("Filter初始化,编码参数: " + encoding);
    }
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                         FilterChain chain) throws IOException, ServletException {
        // 前置处理
        System.out.println("请求到达Filter");
        
        // 放行
        chain.doFilter(request, response);
        
        // 后置处理
        System.out.println("响应离开Filter");
    }
    
    @Override
    public void destroy() {
        // 销毁操作
        System.out.println("Filter销毁");
    }
}

常用URL模式

模式含义示例
/*所有请求全局字符编码
/user/*以/user/开头的请求登录验证
/admin/*以/admin/开头的请求权限控制
*.jsp所有JSP页面页面权限

方式二:web.xml配置

<web-app>
    <!-- Filter定义 -->
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>com.example.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    
    <!-- Filter映射 -->
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

多个Filter时,web.xml中的配置顺序决定了执行顺序;注解配置时,Filter名称的字典序影响顺序(建议使用web.xml精确控制)。

常见问题与解决方案

Filter相关问题

Filter不生效怎么办?

排查步骤

  1. 检查URL模式:确认请求的URL匹配Filter的url-pattern
  2. 检查配置方式:注解和web.xml不要重复配置,可能冲突
  3. 检查是否放行:确认调用了chain.doFilter()
  4. 检查执行顺序:可能被前面的Filter拦截了

多个Filter的执行顺序怎么控制?

解决方案

配置方式顺序控制方法
web.xml<filter-mapping>的配置顺序
注解无法控制(按类名字典序),建议使用web.xml

推荐在web.xml中集中配置Filter顺序:

<!-- 先执行字符编码 -->
<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<!-- 再执行登录验证 -->
<filter-mapping>
    <filter-name>LoginCheckFilter</filter-name>
    <url-pattern>/user/*</url-pattern>
</filter-mapping>

Filter中如何获取Spring容器中的Bean?

// 通过WebApplicationContext获取
WebApplicationContext context = WebApplicationContextUtils
    .getRequiredWebApplicationContext(request.getServletContext());
UserService userService = context.getBean(UserService.class);

Listener相关问题

contextInitialized执行两次?

原因:Tomcat配置了多个Context,或IDEA热部署导致。

解决:在方法中添加判断,避免重复初始化:

@Override
public void contextInitialized(ServletContextEvent sce) {
    ServletContext context = sce.getServletContext();
    // 检查是否已初始化
    if (context.getAttribute("SqlSessionFactory") != null) {
        return;  // 已存在,跳过
    }
    // 初始化代码...
}

Listener中获取不到数据库连接?

原因:数据库驱动在Listener初始化时还未加载。

解决:确保数据库驱动JAR在WEB-INF/lib中,或使用延迟加载。

其他常见问题

如何设置Filter的初始化参数?

注解方式

@WebFilter(
    value = "/*",
    initParams = {
        @WebInitParam(name = "encoding", value = "UTF-8"),
        @WebInitParam(name = "forceEncoding", value = "true")
    }
)
public class CharacterEncodingFilter implements Filter {
    private String encoding;
    
    @Override
    public void init(FilterConfig config) throws ServletException {
        this.encoding = config.getInitParameter("encoding");
    }
}

如何排除特定路径不进行过滤?

@Override
public void doFilter(ServletRequest request, ServletResponse response, 
                     FilterChain chain) throws IOException, ServletException {
    HttpServletRequest httpRequest = (HttpServletRequest) request;
    String path = httpRequest.getRequestURI();
    
    // 排除静态资源
    if (path.startsWith("/static/") || path.endsWith(".js") || path.endsWith(".css")) {
        chain.doFilter(request, response);
        return;
    }
    
    // 继续过滤逻辑...
}

总结

三大组件协作图

┌─────────────────────────────────────────────────────────┐
│                    应用程序生命周期                        │
├─────────────────────────────────────────────────────────┤
│  应用启动                                                │
│     ↓                                                   │
│  ServletContext初始化                                    │
│     ↓                                                   │
│  Listener.contextInitialized()  ← 初始化全局资源          │
│     ↓                                                   │
│  等待请求...                                              │
│     ↓                                                   │
│  请求到达 ───────────────────────────────────────┐       │
│     ↓                                            │       │
│  Filter1.doFilter()前半部分                       │       │
│     ↓                                            │       │
│  Filter2.doFilter()前半部分                       │       │
│     ↓                                            │       │
│  chain.doFilter() ───────────────────────────────┤───────┤
│     ↓                                            │       │
│  Servlet.service()                                │       │
│     ↓                                            │       │
│  Filter2.doFilter()后半部分                       │       │
│     ↓                                            │       │
│  Filter1.doFilter()后半部分 ←────────────────────┘       │
│     ↓                                                   │
│  响应返回                                                │
│     ↓                                                   │
│  应用关闭                                                │
│     ↓                                                   │
│  Listener.contextDestroyed()  ← 清理资源                 │
└─────────────────────────────────────────────────────────┘

知识点回顾

组件核心接口关键方法主要用途
ListenerServletContextListenercontextInitialized() contextDestroyed()应用启动/关闭时执行初始化和清理
FilterFilterinit() doFilter() destroy()请求预处理、响应后处理
  1. 资源初始化:使用Listener在应用启动时初始化数据库连接池、缓存等
  2. 通用处理:使用Filter统一处理字符编码、登录验证、日志记录
  3. 顺序控制:多个Filter时使用web.xml精确控制执行顺序
  4. 异常处理:Filter中注意异常处理,避免影响正常请求
  5. 白名单机制:登录验证等场景使用白名单放行特定路径

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇