servlet容器的3步操作
- 创建一个request对象
- 创建一个调用Servlet的response对象,用来向Web客户端发送响应
- 调用Servlet的service()方法
Catalina是一种Servlet容器
Catalina的两个模板:连接器(connector)和容器(container)
连接器:负责将一个请求与容器相关联(为每一个接收到的Http请求创建一个request对象和一个response对象,然后将处理过程交给容器)
容器:从连接器中接收request对象和response对象,并负责调用响应的Servlet的service对象
连接器
连接器的基本操作
连接服务器
public class HttpConnector implements Runnable {
boolean stopped;
private String scheme = "http";
@Override
public void run() {
ServerSocket serverSocket = null;
int port = 8080;
try {
serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
while (!stopped) {
Socket socket = null;
try {
socket = serverSocket.accept();
} catch (IOException e) {
e.printStackTrace();
}
HttpProcessor processor = new HttpProcessor(this);
processor.process(socket);
}
}
public void start() {
Thread thread = new Thread(this);
thread.start();
}
}
处理
public class HttpProcessor {
private HttpConnector httpConnector;
private HttpRequest request;
private HttpResponse response;
public HttpProcessor(HttpConnector httpConnector) {
this.httpConnector = httpConnector;
}
public void process(Socket socket) {
SocketInputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = new SocketInputStream(socket.getInputStream(), 2048);
outputStream = socket.getOutputStream();
request = new HttpRequest(inputStream);
response=new HttpResponse(outputStream);
response.setRequest(request);
response.setHeader("Server", "Jsq Servlet Container");
parseRequest(inputStream, outputStream);
parseHeader(inputStream);
if (request.getRequestURI().startWith("/servlet/")) {
ServletProcessor processor = new ServletProcessor();
processor.process(request,response);
}else{
StaticResourceProcessor processor = new StaticResourceProcessor();
processor.process(request, response);
}
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Tomcat中的连接器
容器
处理请求servlet资源,并为Web客户端填充填充response对象
容器与连接器的连接关系
HttpConnector connector = new HttpConnector();
SimpleContainer container = new SimpleContainer();
connector.setContainer(container);
4种类型的容器(Engine、Host、Context、Wrapper)
- Engine:表示整个Catalina servlet引擎
- Host:表示包含有一个或多个context容器的虚拟主机
- Context:表示一个Web应用程序,一个Context可以有多个Wrapper
- Wrapper:表示一个独立的servlet
Engine>Host>Context>Wrapper(包含关系)
容器中的管道任务
4个接口:Pipeline(addValve方法添加阀)、Valve(阀,用来处理接收到的请求)、ValveContext(实现依次调用阀)、Contained(限制至多与一个servlet容器相关联)
一个servlet容器中有一条管道,当调用容器的invoke方法,容器会将处理工作交给管道处理,管道会从第一个阀开始,依次执行任务,知道所有任务执行完成为止(调用ValveContext实例的invokeNext方法)
Wrapper(一个独立的servlet)
- 管理基础servlet生命周期(调用servlet的init()、service()、destroy()等方法)
- Wrapper是最低级的servlet容器,不能再向其中添加子容器
- load()载入并初始化servlet类
- allocate()分配一个已经初始化的servlet实例
一个Wrapper的实现
public static void main(String[] args) {
HttpConnector connector = new HttpConnector();
SimpleWrapper wrapper = new SimpleWrapper();
wrapper.setServletClass("ModernServlet");
SimpleLoader loader = new SimpleLoader();
HeaderLoggerValve headerLoggerValve = new HeaderLoggerValve();
ClientIPLoggerValve clientIPLoggerValve = new ClientIPLoggerValve();
wrapper.setLoader(loader);
((Pipeline) wrapper).addValve(headerLoggerValve);
((Pipeline) wrapper).addValve(clientIPLoggerValve);
connector.setContainer(wrapper);
try {
connector.initialize();
connector.start();
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
}
Context(一个Web应用程序)
- 一个Context实例可以有一个或多个Wrapper实例作为子容器
- addWrapper()方法和createWrapper()方法
一个Context实现
public static void main(String[] args) {
HttpConnector connector = new HttpConnector();
SimpleWrapper wrapper1 = new SimpleWrapper();
wrapper1.setName("Modern");
wrapper1.setServletClass("ModernServlet");
SimpleWrapper wrapper2 = new SimpleWrapper();
wrapper2.setName("Primitive");
wrapper2.setServletClass("Primitive");
SimpleContext context = new SimpleContext();
context.addChild(wrapper1);
context.addChild(wrapper2);
HeaderLoggerValve headerLoggerValve = new HeaderLoggerValve();
ClientIPLoggerValve clientIPLoggerValve = new ClientIPLoggerValve();
context.setLoader(loader);
((Pipeline) context).addValve(headerLoggerValve);
((Pipeline) context).addValve(clientIPLoggerValve);
SimpleContextMapper mapper = new SimpleContextMapper();
mapper.setProtocol("http");
context.addMapper(mapper);
SimpleLoader loader = new SimpleLoader();
context.setLoader(loader);
context.addServletMapping("/Primitive","Primitive");
context.addServletMapping("/Modern","Modern");
connector.setContainer(context);
try {
connector.initialize();
connector.start();
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
}
原理
-
容器中包含一个管道,容器的invoke方法会调用管道的invoke方法(如:SimpleContext类的invoke方法调用pipeline的invoke方法)
-
管道的invoke方法会调用所有添加到其容器中的阀,再调用其基础阀的invoke方法(pipeline的invoke方法调用pipelineValveContext的invokeNext,包含ContextValve的invoke方法)
-
在Wrapper实例中,基础阀负责载入相关联的servlet类,并对请求进行响应(ContextValve的invoke方法)
-
在包含子容器的Context实例中,基础阀使用映射器来查找一个子容器,该子容器负责处理接收到的请求,若找到相应的子容器,则调用器invoke方法,转到步骤1继续执行(ContextValve的invoke方法中,context.map(request,true),分配servlet实例,并调用它的service()方法)
-
context.map方法中
String name = context.findServletMapping(relativeURI) wrapper = (Wrapper) context.findChild(name)
start具体步骤
Host(包含有一个或多个context容器的虚拟主机)
若一个Context实例使用ContextConfig对象进行设置,就必须使用一个Host对象,使用ContextConfig对象需要知道应用程序web.xml文件的位置,这里代码显示Context实例需要一个Host实例作为其父容器
StandardHost host = new StandardHost();
host.addChild(context);
host.setName("localhost");
host.setAppBase("webapps");
connector.setContainer(host);
Engine(一个独立的servlet)
支持多个Host(虚拟机)
SimpleEngine engine = new SimpleEngine();
engine.addChild(host);
engine.setDefaultHost("localhost");
connector.setContainer(engine);
容器的生命周期管理
Catalina启动或关闭的时候,它的组件也一起启动或关闭
Catalina允许一个组件包含其他组件,Catalina启动类只要启动一个组件便可以将应用程序的所有组件启动
Lifecycle、LifecycleEvent、LifecycleListener、LifecycleSupport
Lifecycle接口
BEFORE_START_EVENT、START_EVENT、AFTER_START_EVENT在组件启动时触发
BEFORE_STOP_EVENT、STOP_EVENT、AFTER_STOP_EVENT在组件关闭时触发
Context实现Lifecycle的方式
public class SimpleContext implements Context public interface Context extends Container public interface Container extends Lifecycle
实现addLifecycleListener、findLifecycleListeners、removeLifecycleListener方法
start方法中(启动组件和子容器)
public void start() throws LifecycleException {
lifecycle.fireLifecycleEvent(BEFORE_INIT_EVENT,null);
started=true;
//启动加载器
if ((loader != null) && (loader instanceof Lifecycle)) {
((Lifecycle) loader).start();
}
//启动子容器
Container children[] = findChildren();
for (int i = 0; i < children.length; i++) {
if (children[i] instanceof Lifecycle) {
((Lifecycle) children[i]).start();
}
}
//启动pipeline
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).start();
lifecycle.fireLifecycleEvent(START_EVENT, null);
}
lifecycle.fireLifecycleEvent(AFTER_INIT_EVENT, null);
}
stop方法中(关闭组件和子容器)
public void stop() throws LifecycleException {
lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT,null);
lifecycle.fireLifecycleEvent(STOP_EVENT,null);
started=false;
//关闭pipeline
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).stop();
}
//关闭子容器
Container children[] = findChildren();
for (int i = 0; i < children.length; i++) {
if (children[i] instanceof Lifecycle) {
((Lifecycle) children[i]).stop();
}
}
//关闭加载器
if ((loader != null) && (loader instanceof Lifecycle)) {
((Lifecycle) loader).stop();
}
lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
}
LifecycleEvent(生命周期事件)
Lifecycle作为入参
LifecycleListener接口(生命周期的事件监听器)
当某个事件监听器监听到相关事件发生时,会调用该方法
LifecycleSupport(帮助管理监听器,并触发相应的生命周期事件)
容器的组件
载入器(Loader)
servlet只允许载入WEB-INF/classes目录及其子目录下的类。和WEB-INFO/lib目录下的库
Logger接口的setContainer和getContainer方法使日志记录器和某个servlet容器相关联
Loader接口的setContainer和getContainer方法使载入器和某个servlet容器相关联
或context的setLoader方法使载入器和某个servlet容器相关联
Loader loader =new WebAppLoader() context.setLoader(loader)
日志记录器(Logger)
Logger接口的setContainer和getContainer方法使日志记录器和某个servlet容器相关联
或context的setLogger方法使日志记录器和某个servlet容器相关联
SystemOutLogger、SystemErrLogger、FileLogger
Bootstrap中
System.setProperties("catalina.base", System.getProperties("user.dir")); FileLogger logger = new FileLogger(); logger.setPrefix("FileLog_"); logger.setSuffix(".txt"); logger.setTimeStamp(true); logger.setDirectory("webroot"); context.setLogger(logger);
Session 管理器(Manager)
Manager接口的setContainer和getContainer方法使管理器和某个servlet容器相关联
或context的setManager方法使管理器和某个servlet容器相关联
领域(Realm)(保证安全性)
一个阀(验证器),对当前用户进行身份验证,验证器调用context的领域对象的authenticate()方法验证
context的setRealm方法使管理器和某个servlet容器相关联
资源(Resource)
服务器组件和服务组件(serve和service)
服务器组件提供一种优雅的方式启动和关闭整个Catalina部署(initialize、start、stop、await方法)(addService方法添加服务组件)
服务组件封装了servlet容器和连接器之间的关系(可以有一个servlet容器和多个连接器组成)
StandardService service = new StandardService();
service.setName("Stand-alone Service");
StandardServer server = new StandardServer();
server.addService(service);
service.setContainer(engine);
service.addConnector(connector);
if (server instanceof Lifecycle) {
try {
server.initialize();
((Lifecycle) server).start();
server.await();
} catch (IOException e) {
e.printStackTrace();
}
}
Digester库(用来将XML文档中的元素转换成java对象)
部署器
用来部署和安装Web应用程序,是Deployer接口的实例
要使用一个Web应用程序,必须要将表示该应用程序的Context实例部署到一个Host实例中。在Tomcat中,Context实例可以用WAR文件的形式部署,也可以将整个Web应用程序复制到Tomcat安装目录下的webapp下,对于部署的每个应用程序,可以在其中包含一个描述符文件,该文件包含Context实例的配置信息,描述符文件采用xml文档格式。
HostConfig类型的生命周期监听器,来将Context实例添加到Host实例中。HostConfig实例的start方法会逐个部署和安装指定目录中的所有Web应用程序,HostConfig类的start方法有deployApps方法,deployApps方法中会调用deployDescriptors()、deployWARs()、deployDirectories()方法来部署Context实例(web应用程序)
关闭钩子
在正常关闭(System.exit()或程序的最后一个非守护进程线程退出)和虚拟机突然中断时,虚拟机启动所有已经注册的关闭钩子(关闭钩子是先前已经通过Runtime类注册的线程),所有关闭钩子会并发执行,直到完成任务。然后虚拟机根据情况调用所有没有被调用过的终结器。(无论用户如何关闭应用程序,清理代码总是能够得到执行)
创建关闭钩子
- 创建一个Thread类的子类,实现子类的run方法
- 在应用程序中实例化关闭钩子类
- 在当前Runtime类的addShutdownHook方法注册关闭钩子
public class ShutdownHook extends Thread{
@Override
public void run() {
System.out.println("shutdown");
}
}
ShutdownHook shutdownHook = new ShutdownHook();
Runtime.getRuntime().addShutdownHook(shutdownHook);
启动Tomcat(Catalina类和Bootstrap类)
Catalina类用于启动和关闭Server对象,并负责解析Tomcat配置文件(server.xml)
Catalina包含一个Digester对象解析server.xml,一个Server对象,Server对象有一个Service对象,Service对象包含一个Servlet容器和一个或多个连接器
Bootstrap类是一个入口点,负责创建Catalina实例,并调用process()方法,作为一个独立的应用程序运行Tomcat。
Manager应用程序的Servlet类
Manager应用程序用于管理已经部署的Web应用程序(如显示已部署Web应用程序信息)
基于JMX的管理
JMX(java manager extensions)