一、tomcat运行原理的简单分析
tomcat运行时可以简单分成两个大的模块,
(1) 负责处理socket连接并转换成request对象的部分,连接器Coyote
(2) 处理用户的请求的容器Catalina
下面简单介绍下这两个部分
1.1 Coyote
Coyote是tomcat的连接器框架的名称,是tomcat服务器提供的供客户端访问的外部接口。客户端通过Coytote与服务器建立连接,发送请求并接收响应。
Coyote封装了底层的网络通信(Scoket),为Catalina容器提供了统一的接口。Coyote将Scoket输入转换封装为Request对象交给Catalina处理,容器处理完后通过Coyote提供的Response对象将结果写入输出流中。
Coyote由如下几个部分组成。
EndPoint: Coyote通信端点,对scoket链接进行处理。
Processor: 把scoket连接中的信息转成Request对象(并不是Servlet规范中的请求对象)
Adapter: 把Request对象转换成Servlet规范中的ServletRequest
1.2 容器 catalina
Catalina是Servlet容器的实现,由Server,Service,Container组成了一个分层结构
Tomcat设计了四种容器,从大到小是父子关系,Engine,Host,Context,Wrapper。
Engine: 一个Service中只能有一个Engine
Host: 虚拟主机,一个Engine中可配置多个Host,通过配置不同的host可以在同一tomcat上部署不同的站点
Context: 表示一个应用,一个host中可以配置多个Context
Wrapper:表示一个Servlet,一个Context中可以有多个Servlet
Tomcat这些组成部分之间的关系都可以在server.xml中找到对应的标签。
二、用编码的方式启动tomcat
这一节用java代码来启动tomcat
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Collections;
import java.util.Set;
public class TomcatTest {
public static void main(String[] args) throws IOException, LifecycleException {
//创建Tomcat对象
Tomcat tomcat = new Tomcat();
//设置基础目录的名称,这个名称并不是磁盘上真正存在的目录
tomcat.setBaseDir("tomcat");
//一个Context表示一个应用,给tomcat中设置一个应用的目录就是添加一个项目,这个目录需要真实存在
File docBase = Files.createTempDirectory("boot.").toFile();
// 第一个参数表示应用的访问路径,给空表示用根路径访问项目
Context context = tomcat.addContext("", docBase.getAbsolutePath());
// 利用Servlet规范中的ServletContainerInitializer接口给Context中添加Servlet,
// Servlet容器启动时会执行被添加的ServletContainerInitializer接口中的onStartup方法
context.addServletContainerInitializer(new ServletContainerInitializer() {
@Override
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
ServletRegistration.Dynamic dynamic = ctx.addServlet("hello", new HttpServlet() {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("<h1>hello tomcat</h1>");
}
});
dynamic.addMapping("/hello");
}
}, Collections.emptySet());
//创建连接器
Connector connector = new Connector("HTTP/1.1");
connector.setPort(8080);
//把连接器设置到tomcat上
tomcat.setConnector(connector);
//启动tomcat
tomcat.start();
}
}
这样就实现了以编码的方式启动一个Tomcat服务,并注册了一个servlet到应用中。
在springboot工程中,spring容器AnnotationConfigServletWebServerApplicationContext
的onRefresh方法中
有类似上边代码的逻辑,用编程的方式启动了一个tomcat。
关于这一点可以看下我的另一篇文章springboot的启动原理和流程