SpringBoot的内置Tomcat是如何启动的?

整体流程

我们知道SpringApplication.run()时会创建根据推断的环境创建对应的应用上下文

protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
				contextClass = Class.forName(this.webEnvironment
						? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
		}
		return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
	}

DEFAULT_WEB_CONTEXT_CLASSAnnotationConfigEmbeddedWebApplicationContext,其继承了EmbeddedWebApplicationContext,因此在web环境中创建的应用上下文为AnnotationConfigEmbeddedWebApplicationContext
在进行刷新上下文onRefresh()中,我们可以看下它的代码

protected void onRefresh() {
		super.onRefresh();
		try {
			createEmbeddedServletContainer();
		}
	}

在调用了Spring的应用上下午刷新后,还进行了createEmbeddedServletContainer即创建内置的Servlet容器操作,其通过EmbeddedServletContainerFactory工厂方法来进行具体的创建工作。

private void createEmbeddedServletContainer() {
		EmbeddedServletContainer localContainer = this.embeddedServletContainer;
		ServletContext localServletContext = getServletContext();
		if (localContainer == null && localServletContext == null) {
			EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
			//这里通过工厂方法来创建Servlet容器,这里会将当前环境中的ServletContextInitializer找出来
			this.embeddedServletContainer = containerFactory
					.getEmbeddedServletContainer(getSelfInitializer());
		}
		else if (localServletContext != null) {
			try {
				//如果已经存在环境则进行初始化
				getSelfInitializer().onStartup(localServletContext);
			}
		}
		initPropertySources();
	}

ServletContextInitializer的查找最终由工具类ServletContextInitializerBeans.addServletContextInitializerBeans()实现

private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
		for (Entry<String, ServletContextInitializer> initializerBean : getOrderedBeansOfType(
				beanFactory, ServletContextInitializer.class)) {
			addServletContextInitializerBean(initializerBean.getKey(),
					initializerBean.getValue(), beanFactory);
		}
	}

我们来到TomcatEmbeddedServletContainerFactory

public EmbeddedServletContainer getEmbeddedServletContainer(
			ServletContextInitializer... initializers) {
		Tomcat tomcat = new Tomcat();
		File baseDir = (this.baseDirectory != null ? this.baseDirectory
				: createTempDir("tomcat"));
		tomcat.setBaseDir(baseDir.getAbsolutePath());
		Connector connector = new Connector(this.protocol);
		tomcat.getService().addConnector(connector);
		customizeConnector(connector);
		tomcat.setConnector(connector);
		tomcat.getHost().setAutoDeploy(false);
		configureEngine(tomcat.getEngine());
		for (Connector additionalConnector : this.additionalTomcatConnectors) {
			tomcat.getService().addConnector(additionalConnector);
		}
		prepareContext(tomcat.getHost(), initializers);
		return getTomcatEmbeddedServletContainer(tomcat);
	}

可以看到创建了一个Tomcat实例并进行了一些配置,核心方法为prepareContext(tomcat.getHost(), initializers);
这里会创建Tomcat的上下文环境TomcatEmbeddedContext并进行一些基础配置,并添加生命周期监听器、资源加载器、设置Servlet并对Servlet环境进行初始化操作。
getTomcatEmbeddedServletContainer中会通过TomcatEmbeddedServletContainer的构造器调用其initialize方法,进行tomcat.start()的调用启动tomcat。

初始化

protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
		File docBase = getValidDocumentRoot();
		docBase = (docBase != null ? docBase : createTempDir("tomcat-docbase"));
		final TomcatEmbeddedContext context = new TomcatEmbeddedContext();
		context.setName(getContextPath());
		context.setDisplayName(getDisplayName());
		context.setPath(getContextPath());
		context.setDocBase(docBase.getAbsolutePath());
		context.addLifecycleListener(new FixContextListener());
		context.setParentClassLoader(
				this.resourceLoader != null ? this.resourceLoader.getClassLoader()
						: ClassUtils.getDefaultClassLoader());
		resetDefaultLocaleMapping(context);
		addLocaleMappings(context);
		try {
			context.setUseRelativeRedirects(false);
		}
		catch (NoSuchMethodError ex) {
			// Tomcat is < 8.0.30. Continue
		}
		SkipPatternJarScanner.apply(context, this.tldSkipPatterns);
		WebappLoader loader = new WebappLoader(context.getParentClassLoader());
		loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName());
		loader.setDelegate(true);
		context.setLoader(loader);
		if (isRegisterDefaultServlet()) {
			addDefaultServlet(context);
		}
		if (shouldRegisterJspServlet()) {
			addJspServlet(context);
			addJasperInitializer(context);
			context.addLifecycleListener(new StoreMergedWebXmlListener());
		}
		context.addLifecycleListener(new LifecycleListener() {

			@Override
			public void lifecycleEvent(LifecycleEvent event) {
				if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
					TomcatResources.get(context)
							.addResourceJars(getUrlsOfJarsWithMetaInfResources());
				}
			}

		});
		//Servlet环境初始化
		ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
		configureContext(context, initializersToUse);
		host.addChild(context);
		postProcessContext(context);
	}
protected void configureContext(Context context,
			ServletContextInitializer[] initializers) {
		TomcatStarter starter = new TomcatStarter(initializers);
		if (context instanceof TomcatEmbeddedContext) {
			// Should be true
			((TomcatEmbeddedContext) context).setStarter(starter);
		}
		//将TomcatStarter塞入环境中供启动后调用进行初始化
		context.addServletContainerInitializer(starter, NO_CLASSES);
		// 其他略过
	}

TomcatStarter是一个ServletContainerInitializer,它持有在创建web环境时传过来的当前环境中的所有ServletContextInitializer数组,并在自己的onStartup方法中遍历调用所有ServletContextInitializer.onStartup()。注意后面直接将TomcatStarter这个ServletContainerInitializer塞入了容器,就不需要根据SCIs的规范将其放置在META-INF/services/javax.servlet.ServletContainerInitializer文件中了。

for (ServletContextInitializer initializer : this.initializers) {
				initializer.onStartup(servletContext);
}

TomcatStarter是在什么时候被调用的?
在这里插入图片描述
我们在StandardContext.startInternal()中发现了调用轨迹。关于ServletContainerInitializer的相关我会再开一篇博客专门介绍

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值