功能目标:
实现 Lifecycle 组件,用于统一管理各个组件的启动、停止等生命周期操作。
实现内容:
定义 Lifecycle 接口,提供
start
和stop
方法,供容器中的 Context、Wrapper 等组件使用,方便容器统一管理不同组件的生命周期。
背景:
在 Web 容器中,不同的组件(如 Servlet、Web 应用等)通常有明确的生命周期,从创建到销毁需要一系列的管理操作。为了简化对这些组件生命周期的管理,可以引入统一的生命周期管理机制。通过定义 Lifecycle
接口,容器能够统一控制组件的启动、停止,确保资源的正确初始化与销毁。
13.1 生命周期管理的设计思路
我们首先需要定义一个 Lifecycle 接口,所有需要管理生命周期的组件(如 Context
、Wrapper
、Servlet
)都可以实现该接口。该接口定义了两个基本方法:
start()
:表示组件的启动操作。stop()
:表示组件的停止操作。
这些组件在容器中启动时,将通过调用 start()
方法进行初始化,停止时通过调用 stop()
方法释放资源。
13.2 定义 Lifecycle 接口
首先定义 Lifecycle
接口,它将为实现生命周期管理的组件提供统一的接口。
package server;
public interface Lifecycle {
void start() throws Exception; // 启动操作
void stop() throws Exception; // 停止操作
}
start()
方法用于启动组件,可能会执行初始化操作。stop()
方法用于停止组件,通常执行清理工作,如释放资源。
13.3 实现 Context 和 Wrapper 的 Lifecycle 管理
我们将 Context 和 Wrapper 组件实现 Lifecycle 接口,以便统一管理它们的生命周期。
13.3.1 实现 StandardWrapper 类
StandardWrapper
是 Servlet 的封装容器,负责管理单个 Servlet 的生命周期。我们通过实现 Lifecycle
接口,来控制 Servlet 的启动和停止。
package com.daicy.minitomcat.core;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class StandardWrapper implements Wrapper {
private Servlet servlet;
private String className;
private ServletConfig servletConfig;
public StandardWrapper(ServletConfig servletConfig, String className) {
this.servletConfig = servletConfig;
this.className = className;
}
@Override
public void start() throws ServletException {
try {
// 加载 Servlet 实例
servlet = (Servlet) Class.forName(className).newInstance();
servlet.init(servletConfig);
} catch (Exception e) {
throw new ServletException("Failed to load servlet", e);
}
}
@Override
public void invoke(HttpServletRequest request, HttpServletResponse response) throws Exception {
servlet.service(request, response);
}
@Override
public void stop() {
if (servlet != null) {
servlet.destroy();
}
}
@Override
public Servlet getServlet() {
return servlet;
}
}
start()
方法通过反射加载 Servlet 类,并创建实例,接着调用init()
方法初始化 Servlet。stop()
方法调用destroy()
销毁 Servlet。
13.3.2 实现 StandardContext 类
StandardContext
是 Web 应用的上下文容器,它管理应用的生命周期,包括加载和卸载多个 Wrapper
。我们同样需要实现 Lifecycle
接口来管理整个 Web 应用的启动和停止。
package com.daicy.minitomcat.core;
import com.daicy.minitomcat.WebXmlServletContainer;
import com.google.common.collect.Lists;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import java.util.*;
import java.util.stream.Collectors;
public class StandardContext implements Context{
private Map<String, Wrapper> wrapperMap = new HashMap<>();
private WebXmlServletContainer config;
public StandardContext(String configFilePath) throws Exception {
config = new WebXmlServletContainer();
config.loadConfig(configFilePath);
start();
}
public Wrapper getWrapper(String servletPath) {
String servletName = config.getServletName(servletPath);
return getWrapperByName(servletName);
}
public Wrapper getWrapperByName(String servletName) {
return wrapperMap.get(servletName);
}
@Override
public void start() throws Exception {
Map<String, ServletConfig> servletConfigMap = config.getServletConfigMap();
for (String className : servletConfigMap.keySet()) {
ServletConfig servletConfig = servletConfigMap.get(className);
Wrapper wrapper = new StandardWrapper(servletConfig, className);
wrapper.start();
wrapperMap.put(servletConfig.getServletName(), wrapper);
}
}
@Override
public void stop() throws Exception {
List<Wrapper> wrappers = getWrappers();
if(null == wrappers){
return ;
}
for (Wrapper wrapper : wrappers){
wrapper.stop();
}
}
@Override
public List<Wrapper> getWrappers() {
return new ArrayList<>(wrapperMap.values());
}
public List<Servlet> getServlets() {
List<Wrapper> wrappers = getWrappers();
if(null == wrappers){
return Lists.newArrayList();
}
return wrappers.stream().map(Wrapper::getServlet).collect(Collectors.toList());
}
public List<String> getServletNames() {
return config.getServletNames();
}
}
start()
方法启动 Web 应用中的所有Wrapper
,即加载所有的 Servlet。stop()
方法停止 Web 应用中的所有Wrapper
,即销毁所有的 Servlet。
13.4 示例功能:启动和停止 Web 应用
现在我们可以测试 Lifecycle
接口的功能,通过启动和停止 Web 应用来验证我们实现的生命周期管理。
package com.daicy.minitomcat;
import com.daicy.minitomcat.core.StandardContext;
import com.daicy.minitomcat.servlet.CustomHttpSession;
import com.daicy.minitomcat.servlet.ServletContextImpl;
import javax.servlet.*;
import javax.servlet.http.HttpSessionEvent;
import java.util.Enumeration;
/**
* @author 代长亚
* 一个简单的 HTTP 服务器,用于处理 GET 请求并返回静态文件。
*/
public class HttpServer {
static final String WEB_ROOT = "webroot"; // 静态文件根目录
public static ServletContextImpl servletContext = new ServletContextImpl();
public static StandardContext context;
public static FilterManager filterManager = new FilterManager();
private static ServletContextListenerManager servletContextListenerManager = new ServletContextListenerManager();
public static HttpSessionListenerManager sessionListenerManager = new HttpSessionListenerManager();
public static void main(String[] args) throws Exception {
servletContextListenerManager.addListener(new ServletContextListenerImpl());
sessionListenerManager.addListener(new HttpSessionListenerImpl());
// 启动监听器
servletContextListenerManager.notifyContextInitialized(new ServletContextEvent(servletContext));
context = new StandardContext("/web.xml");
context.start();
filterManager.addFilter(new LoggingFilter());
HttpConnector connector = new HttpConnector();
connector.start();
// 模拟服务器关闭
Runtime.getRuntime().addShutdownHook(new Thread(HttpServer::stop));
}
public static void stop() {
try {
System.out.println("Server stopping...");
context.stop();
servletContextListenerManager.notifyContextDestroyed(new ServletContextEvent(servletContext));
SessionManager.removeSession();
} catch (Exception e) {
e.printStackTrace();
}
}
}
在这个示例中,我们创建了一个 Context
实例并向其中添加了多个 Wrapper
,每个 Wrapper
关联一个 Servlet 类。然后调用 context.start()
启动应用,context.stop()
停止应用。
13.5 学习收获
通过实现生命周期管理,我们学到了以下几点:
统一的生命周期管理:通过
Lifecycle
接口,我们实现了一个统一的方式来管理组件的启动和停止。容器可以通过统一接口控制各个组件的生命周期,简化了代码结构。Web 应用的生命周期:通过
Context
和Wrapper
类的管理,我们能够模拟 Web 应用和 Servlet 的生命周期,理解 Web 容器如何管理多个 Servlet 的生命周期。组件化管理:生命周期管理让 Web 容器的各个组件可以独立管理,提升了系统的可扩展性和灵活性。
通过生命周期管理机制,我们为 Web 容器提供了更加清晰、规范的组件管理流程,使得每个组件的生命周期操作更加明确。
项目源代码地址:
https://github.com/daichangya/MiniTomcat/tree/chapter13/mini-tomcat