四、嵌入式Servlet容器自动配置原理

注意:本次自动配置原理基于SpringBoot 1.X版本,其中部分类在2.X版本有所变化,但是具体的流程和原理都是相似的,重要的是观察原理

(一)Servlet容器启动过程

org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration

@Configuration
	@ConditionalOnClass({ Servlet.class, Tomcat.class })  
	@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
	public static class EmbeddedTomcat {

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

	}

其中@Configuration表示如下是一个JAVA配置
@ConditionalOnClass({ Servlet.class, Tomcat.class }) 判断是否引入了有Servlet依赖Tomcat依赖
@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT) 判断当前容器中没有用户自己定义的嵌入式容器工厂(创建嵌入式的Servlet),如果没有就启用下面的配置

嵌入式的容器工厂有如下几个(1.X版本):
进入EmbeddedServletContainerFactory,按下CTRL+H观察右侧可以发现共有三个不同的容器工厂

嵌入式的容器工厂有如下几个(1.X版本):
进入EmbeddedServletContainerFactory,按下CTRL+H观察右侧可以发现共有三个不同的容器工厂

在这里插入图片描述
上面几个不同的工厂也对应了其容器,EmbeddedServletContainer

public interface EmbeddedServletContainer {
/**
	 * Starts the embedded servlet container. Calling this method on an already started
	 * container has no effect.
	 * @throws EmbeddedServletContainerException if the container cannot be started
	 */
	void start() throws EmbeddedServletContainerException;

	/**
	 * Stops the embedded servlet container. Calling this method on an already stopped
	 * container has no effect.
	 * @throws EmbeddedServletContainerException if the container cannot be stopped
	 */
	void stop() throws EmbeddedServletContainerException;

	/**
	 * Return the port this server is listening on.
	 * @return the port (or -1 if none)
	 */
	int getPort();}

在这里插入图片描述
根据上面的观察,我们就能够明白,SpringBoot就是根据我们依赖文件的不同去启动不同的工厂和容器,默认情况下依赖tomcat,因此我们启动的时候使用的是tomcat的factory,启动的容器也就是tomcat容器

接下来主要看一下tomcat容器的启动过程:

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 {

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

	}

这里初始化了TomcatEmbeddedServletContainerFactory()类,进入这个类后我们看下下面这个方法

@Override
	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);
	}

这里面首先 new Tomcat();创建了一个tomcat实例,设置了项目路径,设置了连接器,配置了引擎,完成上述工作后调用了getTomcatEmbeddedServletContainer

protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
			Tomcat tomcat) {
		return new TomcatEmbeddedServletContainer(tomcat, getPort() >= 0);
	}

将前面的tomcat对象传递过来后,这里面只做了一个端口是否大于等于0的判断,接下来就是调用了initialize方法,在这个方法中this.tomcat.start();启动了传入的tomcat容器

private void initialize() throws EmbeddedServletContainerException {
		TomcatEmbeddedServletContainer.logger
				.info("Tomcat initialized with port(s): " + getPortsDescription(false));
		synchronized (this.monitor) {
			try {
				addInstanceIdToEngineName();
				try {
					// 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 = 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) {
					containerCounter.decrementAndGet();
					throw ex;
				}
			}
			catch (Exception ex) {
				throw new EmbeddedServletContainerException(
						"Unable to start embedded Tomcat", ex);
			}
		}
	}
(二)嵌入式容器的配置修改如何生效

对于嵌入式的Servlet容器修改主要有俩种方式:
1:配置文件修改,对应于ServerProperties
2:自定义的定制器:EmbeddedServletContainerCustomizer

具体流程:

1:SpringBoot根据导入的依赖情况,给容器中添加相应的嵌入式Servlet工厂,EmbeddedServletContainerFactory

2:容器中某个组件要创建对象就会触发后置处理器
EmbeddedServletContainerCustomizerBeanPostProcessor,只要是嵌入式的Servlet容器工厂,后置处理器都会处理

3:后置处理器,从容器中获取所有的EmbeddedServletContainerCustomizer,调用定制器的定制方法customize

原创文章 105 获赞 33 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Octopus21/article/details/104841058