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() 方法

容器会调用 ServletContextListenercontextDestroyed() 方法,通知应用即将关闭。

示例代码(监听器销毁):

@Override
public void contextDestroyed(ServletContextEvent sce) {
    System.out.println("Application is stopping...");
    // 清理资源
}

五、关键接口与类

  • Servlet:核心接口,定义了 init()service()destroy() 方法。
  • Filter:用于拦截请求和响应。
  • Listener:用于监听应用生命周期事件。
  • ServletContext:全局上下文对象,存储共享数据和资源。
posted @   软件职业规划  阅读(10)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示