目录:Springboot源码学习目录上文:11、SpringBoot 启动 刷新应用上下文 自动装配解析(三)前言:好了,终于将刷新应用上下文的自动装配流程的源码读完了,顺便也将Spring的配置类解析过程回忆了一遍,现在我们要回过头,继续回到应用上下文刷新的主流程的下一个重点,Web容器的创建和启动 我们应该还记得09、SpringBoot 启动 - 刷新应用上下文 仅自己可见 这篇文章里,refresh方法的源码中,我们标注过里面调用了一个方法onRefresh() 这个方法里面就隐藏着我们的神秘的SpringBoot和Tomcat之类的Web容器整合过程,让我们一睹芳容

在开始debu源码之前,我们先要提前认识三个重要的配置类,这三个配置类都是在SpringBoot自动装配时注入的
ServletWebServerFactoryAutoConfigurationErrorMvcAutoConfigurationDispatcherServletAutoConfiguration 以及这三个配置类分别又导入的bean(没有列出导入的所有bean,只列一部分启动时就用到的)

  1. ServletWebServerFactoryAutoConfiguration:导入了EmbeddedTomcatBeanPostProcessorsRegistrarServletWebServerFactoryCustomizerTomcatServletWebServerFactoryCustomizer
  2. EmbeddedTomcat:主要作用就是向beanFactory中注册一个类型是TomcatServletWebServerFactory的bean,
  3. ServletWebServerFactoryCustomizer:实现了WebServerFactoryCustomizer接口,对EmbeddedTomcat注册的TomcatServletWebServerFactory进行配置
  4. TomcatServletWebServerFactoryCustomizer:实现了WebServerFactoryCustomizer接口,对EmbeddedTomcat注册的TomcatServletWebServerFactory进行配置
  5. BeanPostProcessorsRegistrar:实现了,主要作用是向beanFactory中注入了两个beanPostProcessor,WebServerFactoryCustomizerBeanPostProcessorErrorPageRegistrarBeanPostProcessor
  6. WebServerFactoryCustomizerBeanPostProcessor:所有的WebServerFactoryCustomizer接口实现类的bean都要被该类增强,上面的ServletWebServerFactoryCustomizerTomcatServletWebServerFactoryCustomizer就实现了WebServerFactoryCustomizer接口
  7. ErrorPageRegistrarBeanPostProcessor:所有的ErrorPageRegistry接口实现类的bean都要被该类增强,增强方式是获取所有的ErrorPageRegistrar接口实现类的bean,ErrorPageRegistry接口实现类的bean进行增强,上面TomcatServletWebServerFactory也实现了这个接口ErrorPageRegistry接口
  8. ErrorMvcAutoConfiguration:主要作用是向beanFactory中注入了类型为ErrorPageCustomizer的bean
    ErrorPageCustomizer:实现了ErrorPageRegistrar接口,ErrorPageRegistrarBeanPostProcessor正是用ErrorPageCustomizer对实现了ErrorPageRegistry接口的bean进行增强
  9. DispatcherServletAutoConfiguration:主要作用是向beanFactory中注入了类型为 DispatcherServletRegistrationBeanDispatcherServlet的bean
  10. DispatcherServletRegistrationBean:依赖DispatcherServlet,必须等DispatcherServlet注入beanFactory后才能创建,实现了ServletContextInitializer接口,很重要,就是靠这个bean将DispatcherServlet这个bean加入serlvet容器中
  11. DispatcherServlet:不用解释,SpringMVC的核心中的核心

一、onRefresh

debug进入org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh

protected void onRefresh() {
                //调用父类的方法,没什么重要的内容我们就不看了
		super.onRefresh();
		try {
                        // 看名字我们就知道,这里是重点,创建Web服务
			createWebServer();
		}
		catch (Throwable ex) {
			throw new ApplicationContextException("Unable to start web server", ex);
		}
	}

debug进入createWebServer方法

private void createWebServer() {
                // 获取Web服务器,第一次启动,为null
		WebServer webServer = this.webServer;
                // 获取Servlet上下文,第一次启动,为null
		ServletContext servletContext = getServletContext();
		if (webServer == null && servletContext == null) {
                        // 第一次启动,上面两个都为null,所以进到这个逻辑里,
                        // 记录时间
			StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
                        // 获取WebServer工厂,重点见标题三
			ServletWebServerFactory factory = getWebServerFactory();
			createWebServer.tag("factory", factory.getClass().toString());
                        // 通过WebServer工厂,获取WebServer,重点见标题四,
                        // getSelfInitializer()方法获取一个实现了ServletContextInitializer接口的对象,也是我们一会要看的重点,见标题二,
                        // 之所以把这个getSelfInitializer方法的源码解读放在标题二,是因为这个方法返回的对象实现了ServletContextInitializer接口,
                        // 是通过lambda表达式创建的,后续Tomcat启动会回调ServletContainerInitializer这个接口的所有实现类,
                        // 其中有一个实现了TomcatStarter,会获取所有的ServletContainerInitializer接口实现类,然后回调执行
                        // 后面又会用到这个对象,会重新回到这个方法调用,为了防止混乱我们先放在前面认识一下,至少有一个映像
			this.webServer = factory.getWebServer(getSelfInitializer());
			createWebServer.end();

                        // 向bean工厂中注册两个bean,和WebServer生命周期有关,我们这里不关注
			getBeanFactory().registerSingleton("webServerGracefulShutdown",
					new WebServerGracefulShutdownLifecycle(this.webServer));
			getBeanFactory().registerSingleton("webServerStartStop",
					new WebServerStartStopLifecycle(this, this.webServer));
		}
		else if (servletContext != null) {
			try {
				getSelfInitializer().onStartup(servletContext);
			}
			catch (ServletException ex) {
				throw new ApplicationContextException("Cannot initialize servlet context", ex);
			}
		}
		initPropertySources();
	}

二、getSelfInitializer()

debug进入getSelfInitializer()方法

// 通过lambda表达式,创建一个org.springframework.boot.web.servlet.ServletContextInitializer对象,里面方法onStartup的实现就是,调用当前方法的selfInitialize()
	private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
		return this::selfInitialize;
	}

我们进入selfInitialize方法看看

private void selfInitialize(ServletContext servletContext) throws ServletException {
		prepareWebApplicationContext(servletContext);
		registerApplicationScope(servletContext);
		WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
                // 核心逻辑就在这里,获取所有的ServletContextInitializer接口的实现类,然后遍历执行onStartup方法,
                // 文章开篇提到的DispatcherServletRegistrationBean,就是一个ServletContextInitializer,在执行回调时会把DispatchServlet加入ServletContext中
		for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
			beans.onStartup(servletContext);
		}
	}

debug进入getServletContextInitializerBeans

protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
                创建ServletContextInitializerBeans对象,本质上是一个集合对象,把当前bean工厂也传进去
		return new ServletContextInitializerBeans(getBeanFactory());
	}

debug进入ServletContextInitializerBeans方法

public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
			Class<? extends ServletContextInitializer>... initializerTypes) {
		this.initializers = new LinkedMultiValueMap<>();
                // 我们没有传initializerTypes,这里就是ServletContextInitializer
		this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
				: Collections.singletonList(ServletContextInitializer.class);
                // 核心逻辑,debug进入
		addServletContextInitializerBeans(beanFactory);
		addAdaptableBeans(beanFactory);
		List<ServletContextInitializer> sortedInitializers = this.initializers.values().stream()
				.flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
				.collect(Collectors.toList());
		this.sortedList = Collections.unmodifiableList(sortedInitializers);
		logMappings(this.initializers);
	}

我们在这里面就可以看到,会将各种类型ServletContextInitializer,分类添加到initializers属性中,最终存储类型有
Servlet,Filter,EventListener,ServletContextInitializer

private void addServletContextInitializerBean(String beanName, ServletContextInitializer initializer,ListableBeanFactory beanFactory) {
		if (initializer instanceof ServletRegistrationBean) {
			Servlet source = ((ServletRegistrationBean<?>) initializer).getServlet();
			addServletContextInitializerBean(Servlet.class, beanName, initializer, beanFactory, source);
		}
		else if (initializer instanceof FilterRegistrationBean) {
			Filter source = ((FilterRegistrationBean<?>) initializer).getFilter();
			addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
		}
		else if (initializer instanceof DelegatingFilterProxyRegistrationBean) {
			String source = ((DelegatingFilterProxyRegistrationBean) initializer).getTargetBeanName();
			addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
		}
		else if (initializer instanceof ServletListenerRegistrationBean) {
			EventListener source = ((ServletListenerRegistrationBean<?>) initializer).getListener();
			addServletContextInitializerBean(EventListener.class, beanName, initializer, beanFactory, source);
		}
		else {
			addServletContextInitializerBean(ServletContextInitializer.class, beanName, initializer, beanFactory,initializer);
		}
	}

三、获取WebServer工厂

protected ServletWebServerFactory getWebServerFactory() {
		// Use bean names so that we don't consider the hierarchy
                // 因为我们使用的是默认的web容器,此处会获取到一个tomcatServletWebServerFactory,就是之前自动装配导入的配置类ServletWebServerFactoryConfiguration中导入的类
		String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
		if (beanNames.length == 0) {
			throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "
					+ "ServletWebServerFactory bean.");
		}
		if (beanNames.length > 1) {
			throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
					+ "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
		}
                // 将web容器实例化,就是创建tomcatServletWebServerFactory类型的这个bean
                // 过程很复杂,这里简单说一下,tomcatServletWebServerFactory在初始化过程中会被ErrorPageRegistrarBeanPostProcessor这个beanPostProcessor回调执行,
                // ErrorPageRegistrarBeanPostProcessor 会先获取到所有实现了ErrorPageRegistrar接口的bean,其中就有ErrorPageCustomizer,
                // 而创建ErrorPageCustomizer这个bean的时候,依赖DispatcherServletRegistrationBean,DispatcherServletRegistrationBean又依赖DispatcherServlet
                // 所以会先创建DispatcherServlet整个bean,然后是DispatcherServletRegistrationBean,ErrorPageCustomizer,最后tomcatServletWebServerFactory初始化完成
		return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
	}

四、通过WebServer工厂获取WebServer

public WebServer getWebServer(ServletContextInitializer... initializers) {
		if (this.disableMBeanRegistry) {
			Registry.disableRegistry();
		}
                // 这里我们朝思暮想的Tomcat出现了
		Tomcat tomcat = new Tomcat();
		File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
		tomcat.setBaseDir(baseDir.getAbsolutePath());
		Connector connector = new Connector(this.protocol);
		connector.setThrowOnFailure(true);
		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);
		}
                // 重点见标题五,准备web服务上下文,这里面会创建一个Tomcat上下文,然后将initializers传入
		prepareContext(tomcat.getHost(), initializers);
                // 重点见标题六,获取Tomcat的webServer,这里tomcat会启动
		return getTomcatWebServer(tomcat);
	}

五、准备web服务上下文

protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
		File documentRoot = getValidDocumentRoot();
                // 创建一个内嵌tomcat上下文
		TomcatEmbeddedContext context = new TomcatEmbeddedContext();

		// 省略中间大量内嵌Tomcat的上下文相关逻辑,和我们这次要看的和Web容器整合关系不大

                // 见标题5.1,将传入的ServletContextInitializer和其他ServletContextInitializer合并
		ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
                // 将上下文加入主机
		host.addChild(context);
                // 见标题5.2,配置上下文
		configureContext(context, initializersToUse);
		postProcessContext(context);
	}

5.1、将传入的ServletContextInitializer和其他ServletContextInitializer合并

protected final ServletContextInitializer[] mergeInitializers(ServletContextInitializer... initializers) {
                // 合并过程很简单,就是创建了几个新的ServletContextInitializer接口实现类,并且将所有的ServletContextInitializer接口实现类放入一个list
		List<ServletContextInitializer> mergedInitializers = new ArrayList<>();
		mergedInitializers.add((servletContext) -> this.initParameters.forEach(servletContext::setInitParameter));
		mergedInitializers.add(new SessionConfiguringInitializer(this.session));
		mergedInitializers.addAll(Arrays.asList(initializers));
		mergedInitializers.addAll(this.initializers);
		return mergedInitializers.toArray(new ServletContextInitializer[0]);
	}

5.2、配置上下文

protected void configureContext(Context context, ServletContextInitializer[] initializers) {
                // 这里创建了一个TomcatStarter,然后加入上下文
                // 他实现了ServletContainerInitializer接口,这里我们不介绍这个接口,只需要知道,Tomcat启动后获取所有的实现了ServletContainerInitializer接口的类,并且执行
		TomcatStarter starter = new TomcatStarter(initializers);
		if (context instanceof TomcatEmbeddedContext) {
			TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context;
			embeddedContext.setStarter(starter);
			embeddedContext.setFailCtxIfServletStartFails(true);
		}
		context.addServletContainerInitializer(starter, NO_CLASSES);
                // 省略中间大量内嵌Tomcat的上下文相关逻辑,和我们这次要看的和Web容器整合关系不大
	}

这里我们只是看一下TomcatStarter内部的实现,非常简单

import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import org.springframework.boot.web.servlet.ServletContextInitializer;

class TomcatStarter implements ServletContainerInitializer {
        // 所有的ServletContextInitializer实现
	private final ServletContextInitializer[] initializers;

	TomcatStarter(ServletContextInitializer[] initializers) {
		this.initializers = initializers;
	}

	@Override
	public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
		try {
                        // 遍历ServletContextInitializer接口实现类,回调执行,其中就有我们本文中标题二的内容
			for (ServletContextInitializer initializer : this.initializers) {
				initializer.onStartup(servletContext);
			}
		}
		catch (Exception ex) {
                        // 省略部分不重要的异常处理逻辑
		}
	}

}

六、获取Tomcat的webServer

protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
                // 创建一个tomcat服务
		return new TomcatWebServer(tomcat, getPort() >= 0, getShutdown());
	}

进入TomcatWebServer

public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
		Assert.notNull(tomcat, "Tomcat Server must not be null");
		this.tomcat = tomcat;
		this.autoStart = autoStart;
		this.gracefulShutdown = (shutdown == Shutdown.GRACEFUL) ? new GracefulShutdown(tomcat) : null;
                // 初始化方法,tomcat启动就在这里
		initialize();
	}

debug进入initialize

private void initialize() throws WebServerException {
		logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
		synchronized (this.monitor) {
			try {
				addInstanceIdToEngineName();

				Context context = findContext();
				context.addLifecycleListener((event) -> {
					if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
						// Remove service connectors so that protocol binding doesn't
						// happen when the service is started.
						removeServiceConnectors();
					}
				});

				// Start the server to trigger initialization listeners
                                // tomcat启动
				this.tomcat.start();

				// We can re-throw failure exception directly in the main thread
				rethrowDeferredStartupExceptions();

				try {
					ContextBindings.bindClassLoader(context, context.getNamingToken(), 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) {
				stopSilently();
				destroySilently();
				throw new WebServerException("Unable to start embedded Tomcat", ex);
			}
		}
	}

七、总流程

springboot如何打开html页面 springboot怎么启动web_Server