Spring Boot提供了对Web服务器的内置支持,会自动配置和创建Web服务器。本文分析最常见的Servlet服务器Tomcat和基于Netty的WebFlux服务器的配置与创建过程。
上下文refresh
在Spring Boot运行流程一文中,提到Spring Boot会刷新应用上下文。而该动作是由Spring Framework提供的AbstractApplicationContext中的refresh方法来实现的。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
/*
* 通过new DefaultListableBeanFactory()的方式创建容器,
* 这一步会加载bean的定义
*/
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.
// 会注册web作用域
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.
/*
* 执行BeanFactoryPostProcessor
*/
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
/*
* 往beanPostProcessors属性中add后置处理器并整理好顺序,便于后续执行
*/
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// Initialize message source for this context.
/*
* 这一步较简单,向容器里注册一个一个事件源的单例Bean:MessageSource
*/
initMessageSource();
// Initialize event multicaster for this context.
/*
* 初始化Spring的事件多播器:ApplicationEventMulticaster
*/
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
/*
* 模板方法,子类会实现
* 比如在Spring Boot中,会在该方法中创建web服务器
*/
onRefresh();
// Check for listener beans and register them.
/*
* 注册事件监听器
*/
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
/*
* ---- 极其重要步骤 ----
* 会实例化单例bean
*/
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// 容器刷新收尾工作
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
contextRefresh.end();
}
}
}
在该方法中会调用onRefresh方法,该方法默认是一个空方法,具体的扩展需要由子类来实现。同样地在Spring Boot运行流程一文中,根据不同的web应用类型创建了不同类型的应用上下文对象。
- 对于Servet创建的是AnnotationConfigServletWebServerApplicationContext,在其直接父类ServletWebServerApplicationContext中重写了onRefresh方法。
- 对于Reactive创建的是AnnotationConfigReactiveWebServerApplicationContext,在其直接父类ReactiveWebServerApplicationContext中重写了onRefresh方法。
@Override
protected void onRefresh() {
super.onRefresh();
try {
// 创建web容器
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
@Override
protected void onRefresh() {
super.onRefresh();
try {
// 创建HttpServer
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start reactive web server", ex);
}
}
下面分别对Servlet和Reactive两种类型的Web容器创建过程进行分析。
Servlet
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
// 如果webServer为null,则新建一个。一般都是这种情况。
if (webServer == null && servletContext == null) {
StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
// 获取WebServer工厂,这里关注TomcatServletWebServerFactory就是了
ServletWebServerFactory factory = getWebServerFactory();
createWebServer.tag("factory", factory.getClass().toString());
// 创建WebServer实例,并传入ServletContainerInitializer
this.webServer = factory.getWebServer(getSelfInitializer());
createWebServer.end();
// 向容器中注入两个bean
getBeanFactory().registerSingleton("webServerGracefulShutdown",
new WebServerGracefulShutdownLifecycle(this.webServer)); // 负责设置running标志位和停止WebServer
getBeanFactory().registerSingleton("webServerStartStop",
new WebServerStartStopLifecycle(this, this.webServer)); // 负责实际启动和停止WebServer
}
else if (servletContext != null) { // 如果servletContext对象存在
try {
// 则通过SCI来初始化
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
initPropertySources();
}
getWebServerFactory方法
protected ServletWebServerFactory getWebServerFactory() {
// Use bean names so that we don't consider the hierarchy
// 获取容器中所有ServletWebServerFactory类型的bean名称
String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
// 如果一个也没有,则抛出异常
if (beanNames.length == 0) {
throw new MissingWebServerFactoryBeanException(getClass(), ServletWebServerFactory.class,
WebApplicationType.SERVLET);
}
// 如果大于1个,也会抛出异常
if (beanNames.length > 1) {
throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
+ "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
}
// 获取WebServerFactory实例
return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}
该方法中获取了bean工厂中类型为ServletWebServerFactory的bean实例。而这个bean实例是怎么添加到工厂中的呢?实际上Spring Boot提供了ServletWebServerFactoryAutoConfiguration这个自动配置类,会通过@Import注解引入一系列的Web服务器工厂类。
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
可以看到这里引入了三种工厂类,分别针对Tomcat、Jetty和Undertow,本文以Tomcat为例。
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedTomcat {
// 注入TomcatServletWebServerFactory实例
@Bean
TomcatServletWebServerFactory tomcatServletWebServerFactory(
ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
ObjectProvider<TomcatContextCustomizer> contextCustomizers,
ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
// 创建工厂类对象
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.getTomcatConnectorCustomizers()
.addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatContextCustomizers()
.addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatProtocolHandlerCustomizers()
.addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
return factory;
}
}
注意该类上面的两个conditional相关注解,必须满足这两个条件,该配置类才会被加载,对于其他两种工厂类的是一样的。
那如果在项目中导入了多种Web服务器的依赖会怎么样?比如同时导入Tomcat和Jetty的依赖,两种工厂类的@ConditionalOnClass注解都满足了要求,但是由于受到@ConditionalOnMissingBean注解的限制,最多只能有一个生效。此时在上面自动配置类上的@Import注解中的类顺序就非常重要,排在前面的会被先加载,对于上面的情况,Tomcat的工厂类在Jetty的工厂类前面,所以同时引入了Tomcat和Jetty的依赖,只有Tomcat会生效,而不会创建Jetty的Web服务器。
好,回到主线,此时getWebServerFactory返回了TomcatServletWebServerFactory类型的对象。
getWebServer方法
接下来调用了Web工厂类的getWebServer方法来获取Web服务器,并传入了一个初始化器。先来看这个初始化器是什么。
// 返回一个SCI对象
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
return this::selfInitialize;
}
// 该方法负责初始化servletContext对象
private void selfInitialize(ServletContext servletContext) throws ServletException {
// 设置上下文attr属性
prepareWebApplicationContext(servletContext);
// 向spring容器中注入ServletContextScope
registerApplicationScope(servletContext);
// 将servletContext对象注入到容器中
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
// 遍历容器中设置的其他Servlet上下文初始化器对象
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
// 执行Servlet上下文初始化器
beans.onStartup(servletContext);
}
}
可以看到,这个初始化器就是对servlet上下文做了相关配置,最重要的一点是执行了Servlet的上下文初始化器,这里就不继续深入了,后续针对具体的初始化器做具体的分析。
上面将该初始化器作为参数传递给了TomcatServletWebServerFactory的getWebServer方法。
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
// 禁用JMX注册
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
// 创建Tomcat实例
Tomcat tomcat = new Tomcat();
// 获取或者创建Tomcat的工作目录
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
// 设置工作目录
tomcat.setBaseDir(baseDir.getAbsolutePath());
for (LifecycleListener listener : this.serverLifecycleListeners) {
tomcat.getServer().addLifecycleListener(listener);
}
// 创建连接器组件
Connector connector = new Connector(this.protocol);
// 设置在失败时抛出异常
connector.setThrowOnFailure(true);
// 获取service组件,不存在则会创建,然后添加连接器组件
tomcat.getService().addConnector(connector);
// 定制化连接器,会设置监听端口
customizeConnector(connector);
// 为tomcat实例设置连接器
tomcat.setConnector(connector);
// 获取Host组件,内部会先获取Engine组件,如果不存在则新建;然后设置主机自动部署任务为false
tomcat.getHost().setAutoDeploy(false);
// 配置Engine组件
configureEngine(tomcat.getEngine());
// 添加额外的连接器组件
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
// 创建Tomcat中的Context组件,并设置其属性,然后将其添加到Host组件下
prepareContext(tomcat.getHost(), initializers);
// 根据Tomcat实例,创建TomcatWebServer实例对象
return getTomcatWebServer(tomcat);
}
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
return new TomcatWebServer(tomcat, getPort() >= 0, getShutdown());
}
protected final File createTempDir(String prefix) {
try {
/*
* 在操作系统的临时目录下创建tomcat的工作目录,
* 在Linux中的例子为:/tmp/tomcat.8080.11102105182777561955,结尾的数字是底层JDK创建临时文件时生成的随机数
*/
File tempDir = Files.createTempDirectory(prefix + "." + getPort() + ".").toFile();
tempDir.deleteOnExit();
return tempDir;
}
catch (IOException ex) {
throw new WebServerException(
"Unable to create tempDir. java.io.tmpdir is set to " + System.getProperty("java.io.tmpdir"), ex);
}
}
在getWebServer方法中创建了的Tomcat对象并做了相关的配置,随后将其作为参数调用getTomcatWebServer来创建TomatWebServer对象。
调用customizeConnector方法会定制化连接器,会设置Tomcat所要监听的端口。端口及其他的信息是通过ServletWebServerFactoryCustomizer的customize方法来设置的。而该方法是在WebServerFactoryCustomizerBeanPostProcessor这个后置处理器的后置处理逻辑中调用到的。 ServletWebServerFactoryCustomizer的customize方法是设置到AbstractConfigurableWebServerFactory类中定义的属性上的,而该类是具体的Web服务器工厂类的父类。
注意Tomcat类是Tomcat提供的,不是Spring Boot中的,而TomcatWebServer是Spring Boot提供的类,是对Tomcat的封装。
创建并配置Tomcat的Context组件
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
File documentRoot = getValidDocumentRoot();
// 创建tomcat中的context组件,该类是Spring Boot中实现的,继承tomcat中的StandardContext
TomcatEmbeddedContext context = new TomcatEmbeddedContext();
if (documentRoot != null) {
context.setResources(new LoaderHidingResourceRoot(context));
}
// 为context组件设置一些属性
context.setName(getContextPath());
context.setDisplayName(getDisplayName());
context.setPath(getContextPath());
File docBase = (documentRoot != null) ? documentRoot : createTempDir("tomcat-docbase");
context.setDocBase(docBase.getAbsolutePath());
context.addLifecycleListener(new FixContextListener());
context.setParentClassLoader((this.resourceLoader != null) ? this.resourceLoader.getClassLoader()
: ClassUtils.getDefaultClassLoader());
resetDefaultLocaleMapping(context);
addLocaleMappings(context);
try {
context.setCreateUploadTargets(true);
}
catch (NoSuchMethodError ex) {
// Tomcat is < 8.5.39. Continue.
}
configureTldPatterns(context);
WebappLoader loader = new WebappLoader();
// 设置类加载器名称
loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName());
loader.setDelegate(true);
// 设置web应用加载器
context.setLoader(loader);
// 是否注册默认servlet,该方法默认返回false
if (isRegisterDefaultServlet()) {
// 注册默认的servlet
addDefaultServlet(context);
}
// JSP相关
if (shouldRegisterJspServlet()) {
addJspServlet(context);
addJasperInitializer(context);
}
// 添加声明周期监听器,用于配置静态资源
context.addLifecycleListener(new StaticResourceConfigurer(context));
// 添加一些默认的初始化器,并和上层传递过来的合并在一起
ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
// 为host添加context子组件
host.addChild(context);
// 将initializers设置到context中
configureContext(context, initializersToUse);
// 默认是空操作
postProcessContext(context);
}
protected void configureContext(Context context, ServletContextInitializer[] initializers) {
/*
* 创建starter对象,这是一个特殊的servlet容器初始化器,封装其他多个servlet上下文初始化器
* 注意区分servlet容器初始化器,和servlet上下文初始化器。
* 前者是servlet规范中的定义,而后者是Spring Boot中的定义。
*/
TomcatStarter starter = new TomcatStarter(initializers);
if (context instanceof TomcatEmbeddedContext) {
TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context;
embeddedContext.setStarter(starter);
embeddedContext.setFailCtxIfServletStartFails(true);
}
// 添加servlet容器初始化器
context.addServletContainerInitializer(starter, NO_CLASSES);
// 添加应用生命周期事件监听器,默认是空列表
for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) {
context.addLifecycleListener(lifecycleListener);
}
// 将上下文Valve组件添加到context中,默认是空列表
for (Valve valve : this.contextValves) {
context.getPipeline().addValve(valve);
}
// 添加错误页
for (ErrorPage errorPage : getErrorPages()) {
org.apache.tomcat.util.descriptor.web.ErrorPage tomcatErrorPage = new org.apache.tomcat.util.descriptor.web.ErrorPage();
tomcatErrorPage.setLocation(errorPage.getPath());
tomcatErrorPage.setErrorCode(errorPage.getStatusCode());
tomcatErrorPage.setExceptionType(errorPage.getExceptionName());
context.addErrorPage(tomcatErrorPage);
}
// 添加mime映射
for (MimeMappings.Mapping mapping : getMimeMappings()) {
context.addMimeMapping(mapping.getExtension(), mapping.getMimeType());
}
// 配置session
configureSession(context);
// 配置cookie处理器
configureCookieProcessor(context);
new DisableReferenceClearingContextCustomizer().customize(context);
// 添加web监听器,默认是空列表
for (String webListenerClassName : getWebListenerClassNames()) {
context.addApplicationListener(webListenerClassName);
}
// 通过servlet上下文自定义器来对上下文进行自定义操作,这里默认有5个
for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) {
// 执行自定义操作
customizer.customize(context);
}
}
这里创建的是TomcatEmbeddedContext,继承了Tomcat中的StandardContext类,然后调用configureContext方法对其进行配置。 这里将所有的servlet上下文初始化器封装进Spring Boot自定义的servlet容器初始化器TomcatStarter中,而在该类的onStartup中,会调用所封装的其他servlet上下文初始化器。
@Override
public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
try {
// 遍历servlet上下文初始化器
for (ServletContextInitializer initializer : this.initializers) {
// 调用其onStartup方法
initializer.onStartup(servletContext);
}
}
catch (Exception ex) {
this.startUpException = ex;
// Prevent Tomcat from logging and re-throwing when we know we can
// deal with it in the main thread, but log for information here.
if (logger.isErrorEnabled()) {
logger.error("Error starting Tomcat context. Exception: " + ex.getClass().getName() + ". Message: "
+ ex.getMessage());
}
}
}
注意区分ServletContextInitializer和ServletContainerInitializer,前者是Spring Boot中的类,后者是Servlet规范定义的类。
启动Tomcat
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();
}
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);
}
}
}
可以看到,在initialize方法中就已经调用了Tomcat类的start方法来启动Tomcat了,后续过程参考Tomcat的启动流程。
启动过程
上面不是以及启动过了吗,那这里的启动过程是什么情况?上面确实是启动过服务器了,但这里的”启动过程“更像是做一些启动后的事情,尽管Spring Boot中的代码将其叫作start。 启动的方式是根据SmartLifecycle来启动的,会调用到WebServerStartStopLifecycle的start方法。
@Override
public void start() {
// 启动WebServer
this.webServer.start();
this.running = true;
// 发布WebServer初始化完成的事件
this.applicationContext
.publishEvent(new ServletWebServerInitializedEvent(this.webServer, this.applicationContext));
}
那SmartLifecycle又是怎么被调用到的呢? 实际上在上面AbstractApplication的refresh方法中,调用了finishRefresh方法。
@SuppressWarnings("deprecation")
protected void finishRefresh() {
// Clear context-level resource caches (such as ASM metadata from scanning).
clearResourceCaches();
// Initialize lifecycle processor for this context.
// 初始化生命周期处理器
initLifecycleProcessor();
// Propagate refresh to lifecycle processor first.
getLifecycleProcessor().onRefresh();
// Publish the final event.
publishEvent(new ContextRefreshedEvent(this));
// Participate in LiveBeansView MBean, if active.
if (!NativeDetector.inNativeImage()) {
LiveBeansView.registerApplicationContext(this);
}
}
这里调用了LifecycleProcessor的onRefresh方法,getLifecycleProcessor方法默认返回的是DefaultLifecycleProcessor。
@Override
public void onRefresh() {
startBeans(true);
this.running = true;
}
private void startBeans(boolean autoStartupOnly) {
Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
Map<Integer, LifecycleGroup> phases = new TreeMap<>();
// 对lifecycle对象phrase进行分组,
lifecycleBeans.forEach((beanName, bean) -> {
if (!autoStartupOnly || (bean instanceof SmartLifecycle && ((SmartLifecycle) bean).isAutoStartup())) {
int phase = getPhase(bean);
// 如果phrases中不存在phase对应的组,
phases.computeIfAbsent(
phase,
// 则创建新的组对象
p -> new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly)
).add(beanName, bean);
}
});
if (!phases.isEmpty()) {
// 执行start方法
phases.values().forEach(LifecycleGroup::start);
}
}
在这里,调用了web服务器的start方法。还是以Tomcat为例:
@Override
public void start() throws WebServerException {
synchronized (this.monitor) {
if (this.started) {
return;
}
try {
addPreviouslyRemovedConnectors();
// 获取Connector
Connector connector = this.tomcat.getConnector();
if (connector != null && this.autoStart) {
// 执行延迟启动时加载时加载
performDeferredLoadOnStartup();
}
checkThatConnectorsHaveStarted();
this.started = true;
logger.info("Tomcat started on port(s): " + getPortsDescription(true) + " with context path '"
+ getContextPath() + "'");
}
catch (ConnectorStartFailedException ex) {
stopSilently();
throw ex;
}
catch (Exception ex) {
PortInUseException.throwIfPortBindingException(ex, () -> this.tomcat.getConnector().getPort());
throw new WebServerException("Unable to start embedded Tomcat server", ex);
}
finally {
Context context = findContext();
ContextBindings.unbindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
}
}
}
Reactive
private void createWebServer() {
WebServerManager serverManager = this.serverManager;
if (serverManager == null) {
StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
// 获取WebServer工厂的名称
String webServerFactoryBeanName = getWebServerFactoryBeanName();
// 从容器中获取WebServer工厂实例
ReactiveWebServerFactory webServerFactory = getWebServerFactory(webServerFactoryBeanName);
createWebServer.tag("factory", webServerFactory.getClass().toString());
// 获取是否延迟初始化,对于NettyReactiveWebServerFactory来说是false
boolean lazyInit = getBeanFactory().getBeanDefinition(webServerFactoryBeanName).isLazyInit();
// 创建WebServerManager对象并传入WebServer工厂,内部会创建WebServer
this.serverManager = new WebServerManager(this, webServerFactory, this::getHttpHandler, lazyInit);
getBeanFactory().registerSingleton("webServerGracefulShutdown",
new WebServerGracefulShutdownLifecycle(this.serverManager.getWebServer()));
getBeanFactory().registerSingleton("webServerStartStop",
/*
* 该bean是一个SmartLifecycle类型实例
* (AbstractApplicationContext#refresh方法的finishRefresh阶段会处理),
* 负责WebServer的启动和关闭
*/
new WebServerStartStopLifecycle(this.serverManager));
createWebServer.end();
}
initPropertySources();
}
getWebServerFactoryBeanName方法
protected String getWebServerFactoryBeanName() {
// Use bean names so that we don't consider the hierarchy
/*
* 至于是哪种类型的server被注入到了bean容器中,参考ReactiveWebServerFactoryAutoConfiguration。
*/
String[] beanNames = getBeanFactory().getBeanNamesForType(ReactiveWebServerFactory.class);
if (beanNames.length == 0) {
throw new MissingWebServerFactoryBeanException(getClass(), ReactiveWebServerFactory.class,
WebApplicationType.REACTIVE);
}
if (beanNames.length > 1) { // 只允许有一个ReactiveWebServerFactory类型的bean
throw new ApplicationContextException("Unable to start ReactiveWebApplicationContext due to multiple "
+ "ReactiveWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
}
return beanNames[0];
}
该方法获取了bean工厂中ReactiveWebServerFactory类型实例的名称。那是什么时候添加了这些类型的bean呢?在ReactiveWebServerFactoryAutoConfiguration这个自动配置类上存在一个@Import注解,会引入几个与Web服务器相关的配置类。
/*
* 这里注入了多个,那么在ReactiveWebServerApplicationContext#getWebServerFactoryBeanName方法中
* 会判断是否只有一个ReactiveWebServerFactory实例,如果有多个会报错,那为什么这里还会注入这么多个?
*
* 这些都是配置类,类上有注解@ConditionalOnMissingBean(ReactiveWebServerFactory.class),
* 如果项目中配置了下面多种serverFactory需要的依赖,那么只有排在下面顺序中前面的才能生效。
* 因为其先导入,后导入的在检测上面的注解时,发现已经存在了ReactiveWebServerFactory类型的bean,所以不会生效。
*/
@Import({ ReactiveWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ReactiveWebServerFactoryConfiguration.EmbeddedJetty.class,
ReactiveWebServerFactoryConfiguration.EmbeddedUndertow.class,
// 会注入NettyReactiveWebServerFactory
ReactiveWebServerFactoryConfiguration.EmbeddedNetty.class,
// 为了测试Reactor-Netty,把tomcat的放在后面
ReactiveWebServerFactoryConfiguration.EmbeddedTomcat.class,
})
以Netty为例:
@Configuration(proxyBeanMethods = false)
// 此注解的目的是避免导入多种serverFactory,如果先导入了其他类型的,那么该配置类不会被加载。
@ConditionalOnMissingBean(ReactiveWebServerFactory.class)
@ConditionalOnClass({ HttpServer.class })
static class EmbeddedNetty {
@Bean
@ConditionalOnMissingBean
ReactorResourceFactory reactorServerResourceFactory() {
return new ReactorResourceFactory();
}
@Bean
NettyReactiveWebServerFactory nettyReactiveWebServerFactory(ReactorResourceFactory resourceFactory,
ObjectProvider<NettyRouteProvider> routes, ObjectProvider<NettyServerCustomizer> serverCustomizers) {
// 创建WebServer工厂对象
NettyReactiveWebServerFactory serverFactory = new NettyReactiveWebServerFactory();
serverFactory.setResourceFactory(resourceFactory);
routes.orderedStream().forEach(serverFactory::addRouteProviders);
serverFactory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));
return serverFactory; // 返回对象并被加载到spring容器中
}
}
这里的两个条件注解和上面Servlet小节中介绍的是一样的,不再赘述。
getWebServerFactory方法
接下来会以上面获取到的Web服务器工厂bean的名称作为参数,调用getWebServerFactory方法来获取Web服务器工厂对象。
protected ReactiveWebServerFactory getWebServerFactory(String factoryBeanName) {
return getBeanFactory().getBean(factoryBeanName, ReactiveWebServerFactory.class);
}
创建WebServerManager
将getHttpHandler方法作为处理器提供者(handlerSupplier)传递给WebServerManager的构造方法。
protected HttpHandler getHttpHandler() {
// Use bean names so that we don't consider the hierarchy
/*
* 从bean工厂中获取请求处理器信息,在HttpHandlerAutoConfiguration中会注入该类型的bean。
*/
String[] beanNames = getBeanFactory().getBeanNamesForType(HttpHandler.class);
// 不能一个都不存在
if (beanNames.length == 0) {
throw new ApplicationContextException(
"Unable to start ReactiveWebApplicationContext due to missing HttpHandler bean.");
}
// 也不能存在多个
if (beanNames.length > 1) {
throw new ApplicationContextException(
"Unable to start ReactiveWebApplicationContext due to multiple HttpHandler beans : "
+ StringUtils.arrayToCommaDelimitedString(beanNames));
}
return getBeanFactory().getBean(beanNames[0], HttpHandler.class);
}
该方法其实就是在向bean工厂中获取HttpHandler类型的实例,该类型的实例是在HttpHandlerAutoConfiguration这个自动配置类中被添加到bean工厂的。
@AutoConfiguration(after = { WebFluxAutoConfiguration.class })
@ConditionalOnClass({ DispatcherHandler.class, HttpHandler.class })
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
@ConditionalOnMissingBean(HttpHandler.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
public class HttpHandlerAutoConfiguration {
@Configuration(proxyBeanMethods = false)
public static class AnnotationConfig {
private final ApplicationContext applicationContext;
public AnnotationConfig(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Bean
public HttpHandler httpHandler(ObjectProvider<WebFluxProperties> propsProvider) {
// 创建HTTP处理器,WebHttpHandlerBuilder属于spring-web模块,其实这里创建的是HttpWebHandlerAdapter,该bean同时也是WebHandler
HttpHandler httpHandler = WebHttpHandlerBuilder.applicationContext(this.applicationContext).build();
WebFluxProperties properties = propsProvider.getIfAvailable();
if (properties != null && StringUtils.hasText(properties.getBasePath())) {
Map<String, HttpHandler> handlersMap = Collections.singletonMap(properties.getBasePath(), httpHandler);
return new ContextPathCompositeHandler(handlersMap);
}
return httpHandler;
}
}
}
至于这个httpHandler是怎么创建和初始化的,后续新写文章来分析。
回到主线,来看看WebServerManager的构造方法:
WebServerManager(ReactiveWebServerApplicationContext applicationContext, ReactiveWebServerFactory factory,
Supplier<HttpHandler> handlerSupplier, boolean lazyInit) {
this.applicationContext = applicationContext;
Assert.notNull(factory, "Factory must not be null");
this.handler = new DelayedInitializationHttpHandler(handlerSupplier, lazyInit);
// 创建WebServer
this.webServer = factory.getWebServer(this.handler);
}
可以看到,内部通过ReactiveWebServerFactory来创建Web服务器。
getWebServer方法
这里以Netty为例:
@Override
public WebServer getWebServer(HttpHandler httpHandler) {
// 创建server,类型是HttpServerBind
HttpServer httpServer = createHttpServer();
/*
* 创建适配器,该适配器是Reactor-Netty和Spring的适配器,
* 在该适配器的apply方法中会调用httpHandler来处理请求,即请求进入了Spring的范畴。
* 至于这里的httpHandler是什么,参考HttpHandlerAutoConfiguration,该类中会注入该类型的bean。
*/
ReactorHttpHandlerAdapter handlerAdapter = new ReactorHttpHandlerAdapter(httpHandler);
// 创建NettyWebServer实例,对httpServer进行了封装,并传入了请求处理器handlerAdapter
NettyWebServer webServer = createNettyWebServer(httpServer, handlerAdapter, this.lifecycleTimeout,
getShutdown());
webServer.setRouteProviders(this.routeProviders);
return webServer;
}
该方法内主要做了下面几件事情:
- 创建HttpServer:HttpServers是project-reactor这个项目提供的类。
- 创建适配器:ReactorHttpHandlerAdapter是project-reactor也这个项目提供的类,后面单独写文章来分析。
- 创建NettyWebServer。
创建HttpServer
private HttpServer createHttpServer() {
// 创建的是HttpServerBind类型的实例
HttpServer server = HttpServer.create();
if (this.resourceFactory != null) {
LoopResources resources = this.resourceFactory.getLoopResources();
Assert.notNull(resources, "No LoopResources: is ReactorResourceFactory not initialized yet?");
// 设置绑定所需的地址信息,this::getListenAddress会返回所要监听的地址和端口信息
server = server.runOn(resources).bindAddress(this::getListenAddress);
}
else {
server = server.bindAddress(this::getListenAddress);
}
// SSL相关
if (getSsl() != null && getSsl().isEnabled()) {
server = customizeSslConfiguration(server);
}
// 压缩相关
if (getCompression() != null && getCompression().getEnabled()) {
CompressionCustomizer compressionCustomizer = new CompressionCustomizer(getCompression());
server = compressionCustomizer.apply(server);
}
// 设置协议
server = server.protocol(listProtocols()).forwarded(this.useForwardHeaders);
// 应用自定义设置
return applyCustomizers(server);
}
private InetSocketAddress getListenAddress() {
if (getAddress() != null) {
return new InetSocketAddress(getAddress().getHostAddress(), getPort());
}
return new InetSocketAddress(getPort());
}
在getListenAddress方法中,又会获取配置的所要监听的地址和端口。和上面Servlet服务器中一样,是由ServletWebServerFactoryCustomizer的customize方法来设置配置信息的。
创建NettyWebServer
NettyWebServer createNettyWebServer(HttpServer httpServer, ReactorHttpHandlerAdapter handlerAdapter,
Duration lifecycleTimeout, Shutdown shutdown) {
return new NettyWebServer(httpServer, handlerAdapter, lifecycleTimeout, shutdown);
}
这里将上面创建的HttpHandler和ReactorHttpHandlerAdapter对象一并传递给NettyWebServer的构造方法。这里通过HttpHandler的channelGroup方法进行底层的创建工作,由于这里进入了project-reactor项目的范畴,所以后面单独写文章分析。 好了,现在创建好了NettyWebServer对象,可以交由getWebServer方法返回了。
启动过程
启动方式和上面Servlet服务器一样,通过SmartLifecycle的方式来启动Web服务器。
@Override
public void start() {
// 启动webServer
this.weServerManager.start();
this.running = true;
}
这里会调用到WebServerManager的start方法:
void start() {
// 初始化请求处理器(特殊的请求处理器,其实是适配器)
this.handler.initializeHandler();
// 启动WebServer
this.webServer.start();
this.applicationContext
.publishEvent(new ReactiveWebServerInitializedEvent(this.webServer, this.applicationContext));
}
这里调用了具体类型的WebServer的start方法,还是以Netty为例。
@Override
public void start() throws WebServerException {
if (this.disposableServer == null) {
try {
// 启动httpServer
this.disposableServer = startHttpServer();
}
catch (Exception ex) {
PortInUseException.ifCausedBy(ex, ChannelBindException.class, (bindException) -> {
if (bindException.localPort() > 0 && !isPermissionDenied(bindException.getCause())) {
throw new PortInUseException(bindException.localPort(), ex);
}
});
throw new WebServerException("Unable to start Netty", ex);
}
if (this.disposableServer != null) { // 启动成功,打印日志
logger.info("Netty started" + getStartedOnMessage(this.disposableServer));
}
// 创建线程
startDaemonAwaitThread(this.disposableServer);
}
}
DisposableServer startHttpServer() {
HttpServer server = this.httpServer;
// 下面这个条件默认是true
if (this.routeProviders.isEmpty()) {
// 设置请求处理器
server = server.handle(this.handler);
}
else {
server = server.route(this::applyRouteProviders);
}
if (this.lifecycleTimeout != null) {
return server.bindNow(this.lifecycleTimeout);
}
/*
* 执行bind操作,HttpServer是reactor-netty提供的,所以离开了spring-boot范畴,
* 最后会一直执行到netty层面的ServerBootstrap#bind()
*/
return server.bindNow();
}
private void startDaemonAwaitThread(DisposableServer disposableServer) {
Thread awaitThread = new Thread("server") {
@Override
public void run() {
// 阻塞直到服务停止,onDispose方法在DisposeChannel类中
disposableServer.onDispose().block();
}
};
awaitThread.setContextClassLoader(getClass().getClassLoader());
awaitThread.setDaemon(false);
// 启动线程
awaitThread.start();
}
在startHttpServer方法中,调用了底层的HttpServer来完成启动过程。在startDaemonAwaitThread方法中创建了一个新线程来等待底层的server结束运行。
总结
本文分析了Spring Boot中Servlet和Reactive两种Web服务器的创建过程,并分别以Tomcat和Netty为例分析了详细创建和启动过程。由于调用链条太长,本文仅仅这对Spring Boot内部的实现进行了分析,相关依赖的项目中的实现后面写文章单独进行分析。