Java Web 应用的生命周期详解
一、Java Web 应用生命周期概述
Java Web 应用的生命周期可以分为三个主要阶段:加载与初始化阶段、请求处理阶段 和 销毁阶段。这些阶段涉及不同的组件(如 Servlet、Filter、Listener 等)以及它们的交互方式。
在实际开发中,理解生命周期对于优化性能、管理资源和设计架构至关重要。接下来,我们将详细分析每个阶段的具体内容,并提供更详细的扩写和代码示例。
二、加载与初始化阶段
当服务器启动时,Web 容器(如 Tomcat、Jetty)会加载并初始化 Java Web 应用。这一阶段的主要任务是为应用创建运行环境。
1. 加载配置文件
容器会读取 web.xml 文件或基于注解的配置信息。web.xml 是传统的部署描述符文件,定义了应用的结构和组件的映射关系。现代开发中,也可以通过注解(如 @WebServlet、@WebFilter)来替代部分配置。
示例代码(基于注解的 Servlet 配置):
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("/hello")
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html");
        resp.getWriter().println("<h1>Hello, World!</h1>");
    }
}
注解的优势:
- 简化配置:无需手动编辑 web.xml文件。
- 动态性:可以在运行时动态加载组件。
2. 创建 ServletContext
容器会为整个应用创建一个全局的上下文对象 ServletContext。这个对象在整个应用生命周期中始终存在,用于存储共享数据和资源。
示例代码(使用 ServletContext 存储数据):
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class MyContextListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("Application is starting...");
        sce.getServletContext().setAttribute("appName", "MyApp"); // 设置全局属性
    }
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("Application is stopping...");
    }
}
在 Servlet 中访问全局属性:
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("/contextExample")
public class ContextExampleServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String appName = getServletContext().getAttribute("appName").toString();
        resp.setContentType("text/html");
        resp.getWriter().println("<h1>Application Name: " + appName + "</h1>");
    }
}
3. 初始化监听器
如果定义了 ServletContextListener,容器会在应用启动时调用其 contextInitialized() 方法。监听器可以用来执行一些初始化逻辑,例如加载配置文件、初始化数据库连接池等。
监听器的作用:
- 初始化资源:加载配置文件、初始化数据库连接池。
- 监控应用状态:记录日志、统计应用启动时间。
示例代码(加载配置文件):
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.util.Properties;
public class ConfigLoaderListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        try {
            Properties props = new Properties();
            props.load(getClass().getClassLoader().getResourceAsStream("config.properties"));
            sce.getServletContext().setAttribute("config", props);
            System.out.println("Configuration loaded successfully.");
        } catch (Exception e) {
            System.err.println("Failed to load configuration: " + e.getMessage());
        }
    }
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("Application is stopping...");
    }
}
4. 初始化过滤器和 Servlet
容器会根据配置顺序初始化所有过滤器和 Servlet。每个 Servlet 的 init() 方法会被调用一次,用于完成初始化工作。
示例代码(Servlet 初始化):
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
@WebServlet("/initExample")
public class InitExampleServlet extends HttpServlet {
    private String message;
    @Override
    public void init() throws ServletException {
        System.out.println("Servlet is initializing...");
        message = getInitParameter("message"); // 获取初始化参数
    }
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html");
        resp.getWriter().println("<h1>" + message + "</h1>");
    }
}
在 web.xml 中配置初始化参数:
<servlet>
    <servlet-name>initExample</servlet-name>
    <servlet-class>com.example.InitExampleServlet</servlet-class>
    <init-param>
        <param-name>message</param-name>
        <param-value>Welcome to My App</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>initExample</servlet-name>
    <url-pattern>/initExample</url-pattern>
</servlet-mapping>
三、请求处理阶段
当客户端发起 HTTP 请求时,容器会根据 URL 映射将请求分发到对应的 Servlet 或 JSP 页面。
1. 匹配 URL 模式
容器会根据 web.xml 或注解中的路径映射找到目标 Servlet。例如,/hello 路径会映射到 HelloServlet。
URL 映射规则:
- 精确匹配:/hello匹配/hello路径。
- 后缀匹配:*.jsp匹配所有以.jsp结尾的路径。
- 通配符匹配:/*匹配所有路径。
2. 执行过滤器链
如果有过滤器,容器会按顺序调用它们的 doFilter() 方法。过滤器可以对请求和响应进行预处理或后处理。
示例代码(过滤器链):
import javax.servlet.*;
import java.io.IOException;
public class LoggingFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("LoggingFilter is initializing...");
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        System.out.println("Before processing request...");
        long startTime = System.currentTimeMillis();
        chain.doFilter(request, response); // 继续处理请求
        long endTime = System.currentTimeMillis();
        System.out.println("Request processed in " + (endTime - startTime) + " ms");
    }
    @Override
    public void destroy() {
        System.out.println("LoggingFilter is being destroyed...");
    }
}
过滤器链的作用:
- 日志记录:记录请求的时间、来源和内容。
- 权限控制:检查用户是否有权限访问特定资源。
- 数据转换:修改请求或响应的内容格式。
3. 调用 Servlet 的服务方法
容器会调用目标 Servlet 的 service() 方法,进而调用 doGet() 或 doPost() 方法。开发者可以在这些方法中编写业务逻辑。
示例代码(Servlet 处理请求):
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("/processRequest")
public class ProcessRequestServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String name = req.getParameter("name"); // 获取请求参数
        if (name == null || name.isEmpty()) {
            name = "Guest";
        }
        resp.setContentType("text/html");
        resp.getWriter().println("<h1>Hello, " + name + "!</h1>");
    }
}
请求参数解析:
- 使用 req.getParameter(String name)获取请求参数。
- 支持多种编码方式(如 UTF-8),确保国际化支持。
4. 生成响应
Servlet 或 JSP 页面会生成响应内容并返回给客户端。响应可以是 HTML、JSON、XML 等格式。
示例代码(JSON 响应):
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;
import java.io.PrintWriter;
@WebServlet("/jsonResponse")
public class JsonResponseServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("application/json");
        resp.setCharacterEncoding("UTF-8");
        PrintWriter writer = resp.getWriter();
        writer.println("{\"status\": \"success\", \"message\": \"Hello, JSON!\"}");
    }
}
JSON 响应的优点:
- 轻量级:适合移动端和 API 开发。
- 跨平台:兼容多种编程语言。
四、销毁阶段
当服务器关闭或应用被卸载时,容器会销毁所有组件。
1. 调用 Servlet 的 destroy() 方法
容器会调用每个 Servlet 的 destroy() 方法,释放资源。
示例代码(Servlet 销毁):
@Override
public void destroy() {
    System.out.println("Servlet is being destroyed...");
    // 释放资源
}
销毁阶段的任务:
- 关闭数据库连接。
- 清理缓存数据。
- 释放占用的内存。
2. 调用过滤器的 destroy() 方法
容器会调用每个过滤器的 destroy() 方法,清理相关资源。
示例代码(过滤器销毁):
@Override
public void destroy() {
    System.out.println("Filter is being destroyed...");
}
3. 调用监听器的 contextDestroyed() 方法
容器会调用 ServletContextListener 的 contextDestroyed() 方法,通知应用即将关闭。
示例代码(监听器销毁):
@Override
public void contextDestroyed(ServletContextEvent sce) {
    System.out.println("Application is stopping...");
    // 清理资源
}
五、关键接口与类
- Servlet:核心接口,定义了 init()、service()和destroy()方法。
- Filter:用于拦截请求和响应。
- Listener:用于监听应用生命周期事件。
- ServletContext:全局上下文对象,存储共享数据和资源。
 
                    
                     
                    
                 
                    
                 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号