SpringBoot嵌入式servlet容器Tomcat的自动配置原理,以及启动过程分析

springboot采用大量的自动配置,开发者通过少量的配置,就可开发spring应用,可以用来开发单个微服务应用,下面来介绍一下springboot的内置servlet的原理。

1)、创建一个springboot工程

导入相关依赖:


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0modelVersion>
	<parent>
		<groupId>org.springframework.bootgroupId>
		<artifactId>spring-boot-starter-parentartifactId>
		<version>2.1.3.RELEASEversion>
		<relativePath/> 
	parent>
	<groupId>com.kuakegroupId>
	<artifactId>springboot02-logartifactId>
	<version>0.0.1-SNAPSHOTversion>
	<name>springboot02-logname>
	<description>Demo project for Spring Bootdescription>

	<properties>
		<java.version>1.8java.version>
	properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-webartifactId>
		dependency>
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-loggingartifactId>
		dependency>
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-testartifactId>
			<scope>testscope>
		dependency>
	dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.bootgroupId>
				<artifactId>spring-boot-maven-pluginartifactId>
			plugin>
		plugins>
	build>
project>

2)、自动配置原理

找到与内置容器先关的自动配置类,我们知道spring-boot的配置类都在spring-boot-autoconfigure-xxx.RELEASE.jar包下,进入web相关的自动配置类,里面有一个配置类叫做EmbeddedServletContainerAutoConfiguration ,翻译也就是嵌入式的servlet容器的自动配置类

SpringBoot嵌入式servlet容器Tomcat的自动配置原理,以及启动过程分析_第1张图片
进入这个类的源码,如下图:

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication
@Import(BeanPostProcessorsRegistrar.class)
public class EmbeddedServletContainerAutoConfiguration {

	/**
	 * Nested configuration if Tomcat is being used.
	 */
	@Configuration
	//判断是否缺少相关jar 如果没有就不创建  springboot默认是支持 tomcat的
	@ConditionalOnClass({ Servlet.class, Tomcat.class })
	@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
	public static class EmbeddedTomcat {

		@Bean
		public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
			return new TomcatEmbeddedServletContainerFactory();
		}

	}

	/**
	 * Nested configuration if Jetty is being used.
	 */
	@Configuration
	//判断是否缺少相关jar 如果没有就不创建
	@ConditionalOnClass({ Servlet.class, Server.class, Loader.class,
			WebAppContext.class })
	@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
	public static class EmbeddedJetty {

		@Bean
		public JettyEmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() {
			return new JettyEmbeddedServletContainerFactory();
		}

	}

	/**
	 * Nested configuration if Undertow is being used.
	 */
	@Configuration
	//判断是否缺少相关jar 如果没有就不创建
	@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
	@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
	public static class EmbeddedUndertow {

		@Bean
		public UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory() {
			return new UndertowEmbeddedServletContainerFactory();
		}
	}

springboot支持三个内置容器 分别是Tomcat,Jetty,Undertow,默认支持的是Tomcat容器
在这里插入图片描述

因为有tomcat的相关jar包,所以容器在启动过程中会创建一个TomcatEmbeddedServletContainerFactory

//创建出一个Tomcat容器的生产工厂
@Configuration
	//判断是否缺少相关jar 如果没有就不创建  springboot默认是支持 tomcat的
	@ConditionalOnClass({ Servlet.class, Tomcat.class })
	@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
	public static class EmbeddedTomcat {

		@Bean
		public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
			return new TomcatEmbeddedServletContainerFactory();
		}

	}

注意在这个类的顶部还有一个注解:@Import(BeanPostProcessorsRegistrar.class)导入了一个类到容器中,看一下这个类的源码:

	public static class BeanPostProcessorsRegistrar
			implements ImportBeanDefinitionRegistrar, BeanFactoryAware {

		private ConfigurableListableBeanFactory beanFactory;

		@Override
		public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
			if (beanFactory instanceof ConfigurableListableBeanFactory) {
				this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
			}
		}

		@Override
		public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
				BeanDefinitionRegistry registry) {
			if (this.beanFactory == null) {
				return;
			}
			registerSyntheticBeanIfMissing(registry,
					"embeddedServletContainerCustomizerBeanPostProcessor",
					EmbeddedServletContainerCustomizerBeanPostProcessor.class);
			registerSyntheticBeanIfMissing(registry,
					"errorPageRegistrarBeanPostProcessor",
					ErrorPageRegistrarBeanPostProcessor.class);
		}

		private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry,
				String name, Class<?> beanClass) {
			if (ObjectUtils.isEmpty(
					this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
				RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
				beanDefinition.setSynthetic(true);
				registry.registerBeanDefinition(name, beanDefinition);
			}
		}

	}

在这个类中(上图),有一个很重要的方法registerBeanDefinitions方法中定义了一个类EmbeddedServletContainerCustomizerBeanPostProcessor在容器中,容器中某组件要创建ContainerFactory对象就会惊动动这个后置处理器EmbeddedServletContainerCustomizerBeanPostProcessor,详细的后面解释
总结一下自动配置干了啥:
1、往容器中添加了一个TomcatEmbeddedServletContainerFactory
2、通过注解添加了EmbeddedServletContainerCustomizerBeanPostProcessor,与容器自定义相关的后置处理器


3)、在启动springboot的时候,内置Tomcat容器是如何启动的

(简要记录一下几个主要的方法)
1)、SpringBoot应用启动运行run方法

@SpringBootApplication
public class MicroCloudServiceConsumer80Application {
    public static void main(String[] args) {
        SpringApplication.run(MicroCloudServiceConsumer80Application.class,args);
    }

2)、创建SpringApplication

// 
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
		return new SpringApplication(sources).run(args);
	}

其中new SpringApplication(sources)

initialize(sources);
private void initialize(Object[] sources) {
		if (sources != null && sources.length > 0) {
			this.sources.addAll(Arrays.asList(sources));
		}
		// 判断是否为web应用
		this.webEnvironment = deduceWebEnvironment();
		// 从类路径下找到META-INF/spring.factories配置的所有ApplicationContextInitializer;然后保存起来
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
		// 从类路径下找到META-INF/spring.factories配置的所有ApplicationListener;然后保存起来
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		// 从配置类中找到有main主方法的配置类
		this.mainApplicationClass = deduceMainApplicationClass();
	}

run方法

public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		FailureAnalyzers analyzers = null;
		configureHeadlessProperty();
		// 从类路径下找到META-INF/spring.factories配置的所有SpringApplicationRunListener
		SpringApplicationRunListeners listeners = getRunListeners(args);
		// 调用所有监听器的start()方法
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			// 表示准备环境 并且当环境准备好之后 调用所有的listener#environmentPrepared 表示环境准备完成
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			Banner printedBanner = printBanner(environment);
			// 根据webEnvironment判断是创建普通ioc容器 还是web的ioc容器
			context = createApplicationContext();
			analyzers = new FailureAnalyzers(context);
			// 准备容器 (主要作用如下:)
			// 1、设置context中添加environment 
			// 2、回调所有ApplicationContextInitializer#initialize方法 
			// 3、回调所有listener#contextPrepared(context)方法
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			// 刷新容器 扫描加载组件 bean等 tomcat容器也就这里面被启动的,稍后介绍
			refreshContext(context);
			// 刷新完成之后
			// 从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调
      	    // ApplicationRunner先回调,CommandLineRunner再回调
			afterRefresh(context, applicationArguments);
			// 回调所有listener#finished(context, exception)方法
			listeners.finished(context, null);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			// 返回ioc容器
			return context;
		}
		catch (Throwable ex) {
			handleRunFailure(context, listeners, analyzers, ex);
			throw new IllegalStateException(ex);
		}
	}

重要的refreshContext(context)方法 这个方法很熟悉,就是传统spring里面的容器刷新方法(以后有时间要记录一下其中每一个方法的作用)

	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				finishRefresh();
			}

3)、执行onRefresh()方法
上面这些方法中 onRefresh()方法是留个子类去实现,然后再回调的,我们用debug模式执行到此处,是调用哪一个方法(debug模式,进入下面代码)

	@Override
	protected void onRefresh() {
		super.onRefresh();
		try {
		// 创建EmbeddedServletContainer
			createEmbeddedServletContainer();
		}
		catch (Throwable ex) {
			throw new ApplicationContextException("Unable to start embedded container",
					ex);
		}
	}

createEmbeddedServletContainer源码如下:

	private void createEmbeddedServletContainer() {
		EmbeddedServletContainer localContainer = this.embeddedServletContainer;
		ServletContext localServletContext = getServletContext();
		// 因为第一次访问 所以localContainer 和localServletContext都为null
		if (localContainer == null && localServletContext == null) {
		// 从容器中获得EmbeddedServletContainerFactory 所以容器创建该对象的时候
	    // 对应的后置处理器(EmbeddedServletContainerCustomizerBeanPostProcessor)会执行相关操作 稍后记录 
			EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
		// 获得容器
			this.embeddedServletContainer = containerFactory
					.getEmbeddedServletContainer(getSelfInitializer());
		}
		else if (localServletContext != null) {
			try {
				getSelfInitializer().onStartup(localServletContext);
			}
			catch (ServletException ex) {
				throw new ApplicationContextException("Cannot initialize servlet context",
						ex);
			}
		}
		initPropertySources();
	}

获得容器containerFactory.getEmbeddedServletContainer代码如下:

	public EmbeddedServletContainer getEmbeddedServletContainer(
			ServletContextInitializer... initializers) {
		// 创建一个tomcat
		Tomcat tomcat = new Tomcat();
		File baseDir = (this.baseDirectory != null ? this.baseDirectory
				: createTempDir("tomcat"));
		// 设置路径属性 下面的方法都是对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);
		// 获得嵌入式的tomcat容器  并且有一句代码:this.tomcat.start()启动tomcat
		return getTomcatEmbeddedServletContainer(tomcat);
	}

我们在回过头看一下EmbeddedServletContainerCustomizerBeanPostProcessor这个后置处理器有什么作用:

	private void postProcessBeforeInitialization(
			ConfigurableEmbeddedServletContainer bean) {
			// 调用每一个EmbeddedServletContainerCustomizer#customize方法
		for (EmbeddedServletContainerCustomizer customizer : getCustomizers()) {
			customizer.customize(bean);
		}
	}

	private Collection<EmbeddedServletContainerCustomizer> getCustomizers() {
		if (this.customizers == null) {
			// 获得容器中所有的EmbeddedServletContainerCustomizerr容器自定义器 用来定制servlet容器
			this.customizers = new ArrayList<EmbeddedServletContainerCustomizer>(
					this.beanFactory
							.getBeansOfType(EmbeddedServletContainerCustomizer.class,
									false, false)
							.values());
			Collections.sort(this.customizers, AnnotationAwareOrderComparator.INSTANCE);
			this.customizers = Collections.unmodifiableList(this.customizers);
		}
		return this.customizers;
	}

这个后置处理器的作用就是,获得所有定制器,然后执行定制器的方法。(可以往容器中添加一个EmbeddedServletContainerCustomizer)来定制嵌入式容器

总结:
1、执行EmbeddedWebApplicationContext的onRefresh()中的createEmbeddedServletContainer()
2、从容器中获得工厂getEmbeddedServletContainerFactory()【TomcatEmbeddedServletContainerFactory创建对象,后置处理器一看是这个对象,就获取所有的定制器来先定制Servlet容器的相关配置】
3、containerFactory.getEmbeddedServletContainer(getSelfInitializer())方法,创建tomcat容器,并且启动【this.tomcat.start()】

你可能感兴趣的:(SpringBoot)