🤵♂️ 个人主页:@香菜的个人主页,加 ischongxin ,备注csdn
✍🏻作者简介:csdn 认证博客专家,游戏开发领域优质创作者,华为云享专家,2021年度华为云年度十佳博主
🐋 希望大家多多支持,我们一起进步!😄
如果文章对你有帮助的话,
欢迎评论 💬点赞👍🏻 收藏 📂加关注+
前言
好久没写了,今天继续,虽然类似的文章在网上也能搜到,但是还是自己记录下,也是实际的去探索下,说一下自己的方法
1、源码探索方法
因为网上有很多类似的文章,所以在自己探索之前肯定是先搜索一番,踩着前人的肩膀更容易一些,大概知道流程是哪些,我们只需要深入细节就好。当然还有一些人上来直接看源码,也不是不可以,只是少了一些指导,会略微费劲一些。
今天我的方法就是直接看别人的文章,然后找到核心的调试点,通过断点去看整个流程,去探索一些细节。
找到了核心类和核心方法,随便创建一个web项目,就可以开心的调试了。
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh
2、探索细节
2.1 web类型的定义
上面堆栈的第4个,也就是run:307 堆栈,看到如下代码
org.springframework.boot.SpringApplication#createApplicationContext
kotlin
protected ConfigurableApplicationContext createApplicationContext() {
return this.applicationContextFactory.create(this.webApplicationType);
}
这里可以看到根据不同的webApplicationType 会创建不同的web服务器
这里的问题是webApplicationType 是怎么赋值的呐?
直接搜索,可以看到构造函数的时候进行赋值的
this.webApplicationType = WebApplicationType.deduceFromClasspath();
ini
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = Collections.emptySet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
this.applicationStartup = ApplicationStartup.DEFAULT;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
点进去看实现,是一个静态的函数,是根据当前路径下的类判断,返回是REACTIVE 还是Servlet
csharp
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
return REACTIVE;
} else {
String[] var0 = SERVLET_INDICATOR_CLASSES;
int var1 = var0.length;
for(int var2 = 0; var2 < var1; ++var2) {
String className = var0[var2];
if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
return NONE;
}
}
return SERVLET;
}
}
2.2 Tomcat和Jetty服务器是怎么判断的
看核心代码
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#createWebServer
kotlin
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = this.getServletContext();
if (webServer == null && servletContext == null) {
StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
ServletWebServerFactory factory = this.getWebServerFactory();
createWebServer.tag("factory", factory.getClass().toString());
this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
createWebServer.end();
this.getBeanFactory().registerSingleton("webServerGracefulShutdown", new WebServerGracefulShutdownLifecycle(this.webServer));
this.getBeanFactory().registerSingleton("webServerStartStop", new WebServerStartStopLifecycle(this, this.webServer));
} else if (servletContext != null) {
try {
this.getSelfInitializer().onStartup(servletContext);
} catch (ServletException var5) {
throw new ApplicationContextException("Cannot initialize servlet context", var5);
}
}
this.initPropertySources();
}
ServletWebServerFactory factory = this.getWebServerFactory();
继续往下跟
ServletWebServerFactory接口有多个实现类,下面标红的3个就是常用的三个web容器(tomcat、jetty,undertom),启动tomcat用的是TomcatServletWebServerFactory
2.3 tomcat的工作目录是怎么初始化的
org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getWebServer
scss
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
Tomcat tomcat = new Tomcat();
File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Iterator var4 = this.serverLifecycleListeners.iterator();
while(var4.hasNext()) {
LifecycleListener listener = (LifecycleListener)var4.next();
tomcat.getServer().addLifecycleListener(listener);
}
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
tomcat.getService().addConnector(connector);
this.customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
this.configureEngine(tomcat.getEngine());
Iterator var8 = this.additionalTomcatConnectors.iterator();
while(var8.hasNext()) {
Connector additionalConnector = (Connector)var8.next();
tomcat.getService().addConnector(additionalConnector);
}
this.prepareContext(tomcat.getHost(), initializers);
return this.getTomcatWebServer(tomcat);
}
这里可以看到tomcat的相关设置。
首先说一下这个 Connector 连接器,Tomcat 有两个核心功能:
处理 Socket 连接,负责网络字节流与 Request 和 Response 对象的转化。 加载和管理 Servlet,以及具体处理 Request 请求。 针对这两个功能,Tomcat 设计了两个核心组件来分别完成这两件事,即:连接器(Connector)和容器(Container)。
整个过程大致就是:Connector 连接器接收连接请求,创建Request和Response对象用于和请求端交换数据,然后分配线程让Engine(也就是Servlet容器)来处理这个请求,并把产生的Request和Response对象传给Engine。当Engine处理完请求后,也会通过Connector将响应返回给客户端。
这里面提到了 Engine,这个是 Tomcat 容器里的顶级容器(Container),我们可以通过 Container 类查看其他的子容器:Engine、Host、Context、Wrapper
3、总结
Tomcat 的启动主要是实例化两个组件:Connector、Container。
Spring Boot 创建 Tomcat 时,会先创建一个根上下文,将 WebApplicationContext 传给 Tomcat;
启动 Web 容器,需要调用 getWebserver(),因为默认的 Web 环境就是 TomcatServletWebServerFactory,所以会创建 Tomcat 的 Webserver,这里会把根上下文作为参数给 TomcatServletWebServerFactory 的 getWebServer();
启动 Tomcat,调用 Tomcat 中 Host、Engine 的启动方法。