SprintBoot 1.X 启动tomcat源码分析,与Tomcat启动war包的不同

一、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类加载器即可。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值