一、Tomcat启动过程分析
1、Tomcat基本结构
tomcat的基本结构是1个server,对应一个service,对应多个Connector,对应一个Engine,配置一个host,一个host里面可以配置多个context,即可以配置多个应用,如下图:
2、Tomcat启动过程类加载
tomcat类加载过程参考我的下一篇博文:
Tomcat类加载方式和SpringBoot嵌入式tomcat的类加载方式分析
3、总结
Tomcat启动过程要使用几乎所有的类加载器,可以配置多个应用,同时启动。
二、SpringBoot嵌入式tomcat启动过程源码分析
SprintBoot 通过嵌入式的tomcat来启动tomcat,比单独部署tomcat启动war更加方便简洁,今天来分析springboot如何启动tomcat的。
1、从spring-boot-autoconnfigure.jar文件开始分析,找到spring.factories文件,打开看到所有自动加载类
2、搜索自动装配的嵌入式servlet容器类EmbeddedServletContainerAutoConfiguration:
3、点击该类,查看这段源码,EmbeddedTomcat嵌入Tomcat类。
/**
* {@link EnableAutoConfiguration Auto-configuration} for an embedded servlet containers.
*
* @author Phillip Webb
* @author Dave Syer
* @author Ivan Sopov
*/
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication
@Import(BeanPostProcessorsRegistrar.class)
public class EmbeddedServletContainerAutoConfiguration {
/**
* Nested configuration if Tomcat is being used.
*/
@Configuration
@ConditionalOnClass({ Servlet.class, Tomcat.class })
@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedTomcat {
// 构造出一个嵌入式tomcat容器工厂类交给spring容器
@Bean
public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
return new TomcatEmbeddedServletContainerFactory();
}
}
4、进入TomcatEmbeddedServletContainerFactory 类
此时进入到spring-boot的源码包内,通过下图spring-boot嵌入式的容器有jetty、tomcat和undertow。
继续分析TomcatEmbeddedServletContainerFactory 源码,来看getEmbeddedServletContainer方法:
@Override
public EmbeddedServletContainer getEmbeddedServletContainer(
ServletContextInitializer... initializers) {
// 实例化一个tomcat
Tomcat tomcat = new Tomcat();
// 设置tomcat目录
File baseDir = (this.baseDirectory != null ? this.baseDirectory
: createTempDir("tomcat"));
tomcat.setBaseDir(baseDir.getAbsolutePath());
// 创建一个连接器
Connector connector = new Connector(this.protocol);
// service 添加连接器,一个service可以添加多个连接器
tomcat.getService().addConnector(connector);
// 设置连接器的相关参数
customizeConnector(connector);
// tomcat设置该连接器
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
// 配置tomcat的engine
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
// 准备context,而且只有一个context
prepareContext(tomcat.getHost(), initializers);
// 创建Container
return getTomcatEmbeddedServletContainer(tomcat);
}
继续点击getTomcatEmbeddedServletContainer(tomcat)方法:
/**
* Factory method called to create the {@link TomcatEmbeddedServletContainer}.
* Subclasses can override this method to return a different
* {@link TomcatEmbeddedServletContainer} or apply additional processing to the Tomcat
* server.
* @param tomcat the Tomcat server.
* @return a new {@link TomcatEmbeddedServletContainer} instance
*/
protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
Tomcat tomcat) {
return new TomcatEmbeddedServletContainer(tomcat, getPort() >= 0);
}
继续点击,进入TomcatEmbeddedServletContainer类,
public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer {
private static final Log logger = LogFactory
.getLog(TomcatEmbeddedServletContainer.class);
private static final AtomicInteger containerCounter = new AtomicInteger(-1);
private final Object monitor = new Object();
private final Map<Service, Connector[]> serviceConnectors = new HashMap<Service, Connector[]>();
private final Tomcat tomcat;
private final boolean autoStart;
/**
* Create a new {@link TomcatEmbeddedServletContainer} instance.
* @param tomcat the underlying Tomcat server
*/
public TomcatEmbeddedServletContainer(Tomcat tomcat) {
this(tomcat, true);
}
/**
* Create a new {@link TomcatEmbeddedServletContainer} instance.
* @param tomcat the underlying Tomcat server
* @param autoStart if the server should be started
*/
public TomcatEmbeddedServletContainer(Tomcat tomcat, boolean autoStart) {
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
initialize();
}
在该类中注意是initialize()来启动tomcat,方法如下:
private void initialize() throws EmbeddedServletContainerException {
TomcatEmbeddedServletContainer.logger
.info("Tomcat initialized with port(s): " + getPortsDescription(false));
synchronized (this.monitor) {
try {
addInstanceIdToEngineName();
// Remove service connectors to that protocol binding doesn't happen yet
removeServiceConnectors();
// Start the server to trigger initialization listeners
this.tomcat.start();
// We can re-throw failure exception directly in the main thread
rethrowDeferredStartupExceptions();
// 查找一个Context,即一个应用
Context context = findContext();
try {
ContextBindings.bindClassLoader(context, getNamingToken(context),
getClass().getClassLoader());
}
catch (NamingException ex) {
// Naming is not enabled. Continue
}
// Unlike Jetty, all Tomcat threads are daemon threads. We create a
// blocking non-daemon to stop immediate shutdown
startDaemonAwaitThread();
}
catch (Exception ex) {
throw new EmbeddedServletContainerException(
"Unable to start embedded Tomcat", ex);
}
}
}
这里的tomcat.start(); 为启动tomcat,继续分析tomcat.start()方法:
public void start() throws LifecycleException {
// 获取server信息
this.getServer();
// 获取连接器信息
this.getConnector();
// 启动server
this.server.start();
}
分析一下这个Tomcat的类,tomcat内容结构包括:server,service,connector,host,basedir
public class Tomcat {
private final Map<String, Logger> pinnedLoggers = new HashMap();
protected Server server;
protected Service service;
protected Engine engine;
protected Connector connector;
protected Host host;
protected int port = 8080;
protected String hostname = "localhost";
protected String basedir;
/** @deprecated */
@Deprecated
protected Realm defaultRealm;
private final Map<String, String> userPass = new HashMap();
private final Map<String, List<String>> userRoles = new HashMap();
private final Map<String, Principal> userPrincipals = new HashMap();
static final String[] silences = new String[]{"。。。"};
private boolean silent = false;
private static final String[] DEFAULT_MIME_MAPPINGS = new String[]{"。。。"};
public Tomcat() {
}
public void setBaseDir(String basedir) {
this.basedir = basedir;
}
public void setPort(int port) {
this.port = port;
}
总结
SpringBoot启动嵌入式的Tomcat过程中,Tomcat只能有一个应用,无法配置多个应用;Tomcat无需执行所有的类加载器,只需要执行WebApp,Jspar类加载器即可。