文章摘要(AI生成)
本文介绍了Spring Boot如何通过对容器的定制化升级来简化项目搭建。在Spring Boot应用中,容器启动时会根据应用类型创建不同的应用容器,如Java应用、Web应用和云原生应用。判断应用类型的逻辑在SpringApplication的构建函数中实现。Spring Boot实现了'All in one'的简便,让我们无需重复搭建项目。
在前几篇文章中,我们已经了解了spring如何简化对象创建,spring MVC如何简化请求处理。其实本质上都是通过对容器的定制化和拓展,省去了我们平时需要重复搭建项目的一些步骤。比如spring MVC就通过对容器的自定义刷新方法和自定义监听器的方式完成了web容器的加载和视图的渲染。
而本篇文章中介绍的spring boot,更是重量级,如果说java语言是‘One for all’的话,那么spring boot真正的让我们在搭建项目中实现了’All in one’的简便。那么它是如何实现的呢?
容器定制再升级
在spring boot应用中,容器启动时,会判断我们的应用类型,到底是java应用、web应用还是云原生应用。之后会根据不同的应用类型去创建不同的应用容器。判断应用类型的逻辑在SpringApplication
的构建函数中:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
构建函数中主要加载了一些spring boot中的默认配置,包括应用类型,默认容器加载器、容器监听器等。容器加载器和容器监听器直接读取的spring.properties
文件,从文件中获取到默认的加载器类和监听器类。应用类型的判断也很简单,如果我们引用类包含DispatcherHandler
且不包含DispatcherServlet
和ServletContainer
则为云原生应用,如果我们引用类不包含Servlet
和ConfigurableWebApplicationContext
则为java应用,否则为web引用。
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
而这个应用类型则决定了我们创建哪种容器:
如果是java应用,则只需要创建普通的IOC容器
如果是web应用,则需要创建含有web环境的web容器
如果是响应式应用,则需要创建含有响应式web环境的服务容器
这三个容器的主要创建流程均为:
- 用户调用启动函数
- 获取spring boot默认配置
- 获取程序启动变量,读取配置文件
- 根据应用类型创建应用容器
- 容器初始化
- 配置容器属性
- 容器刷新
- 自定义刷新:创建应用内嵌服务器
- 启动服务器,并监听容器完成事件,完成应用启动
java应用所需容器很简单,这里我们不多做赘述。我们重点来看下后两个的创建流程。
WEB容器
在web容器的自定义刷新方法中,spring boot主要做了两件事:
- 内嵌web服务器的配置
- 获取servlet Context
而内嵌web服务器,我们可以引入不同的服务器包来切换我们的web服务器选型。
对于 servlet应用程序,spring-boot-starter-web 通过包含 spring-boot-starter-tomcat 来包含 Tomcat,但您可以改用 spring-boot-starter-jetty 或 spring-boot-starter-undertow。
对于云原生应用程序,spring-boot-starter-webflux 通过包含 spring-boot-starter-reactor-netty 来包含 Reactor Netty,但您可以使用 spring-boot-starter-tomcat、spring-boot-starter-jetty 或 spring -boot-starter-undertow 代替。
web容器自定义刷新的代码如下:
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
//创建web服务器
private void createWebServer() {
WebServer webServer = this.webServer;
//获取已有的ServletContext
ServletContext servletContext = getServletContext();
//如果webserver不存在且servletContext为空,则创建webserver
if (webServer == null && servletContext == null) {
//创建websever
StartupStep createWebServer = getApplicationStartup().start("spring.boot.webserver.create");
ServletWebServerFactory factory = getWebServerFactory();
createWebServer.tag("factory", factory.getClass().toString());
this.webServer = factory.getWebServer(getSelfInitializer());
createWebServer.end();
//注册webserver
getBeanFactory().registerSingleton("webServerGracefulShutdown",
new WebServerGracefulShutdownLifecycle(this.webServer));
getBeanFactory().registerSingleton("webServerStartStop",
new WebServerStartStopLifecycle(this, this.webServer));
}
//如果servletContext不为空,则对servletContext进行初始化
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
//将servletContext配置到容器中
initPropertySources();
}
web容器自定义刷新首先创建了内嵌的web服务器,然后对servletContext进行初始化,设置到容器中。
从这里可以看到,与MVC中的容器不同的是,MVC中的servletContext在容器初始化时就已经设置到容器中,而Spring boot中的servletContext则在bean工厂构建之后,监听器注册之前完成。
响应式容器
响应式容器的自定义刷新方法,实现与web容器作用相同,都是创建web服务器。
响应式服务器使用webServerManager来管理web服务器,而为了适配不同的 WebServer 请求响应体,Spring设计了HttpHandler用来转化底层的Http请求响应语义,用来接收处理底层容器的Http请求,每一个web服务器都有对应的httpHandler。
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start reactive web server", ex);
}
}
private void createWebServer() {
WebServerManager serverManager = this.serverManager;
if (serverManager == null) {
//创建serverManager
StartupStep createWebServer = getApplicationStartup().start("spring.boot.webserver.create");
String webServerFactoryBeanName = getWebServerFactoryBeanName();
ReactiveWebServerFactory webServerFactory = getWebServerFactory(webServerFactoryBeanName);
createWebServer.tag("factory", webServerFactory.getClass().toString());
boolean lazyInit = getBeanFactory().getBeanDefinition(webServerFactoryBeanName).isLazyInit();
this.serverManager = new WebServerManager(this, webServerFactory, this::getHttpHandler, lazyInit);、
//注册serverManager
getBeanFactory().registerSingleton("webServerGracefulShutdown",
new WebServerGracefulShutdownLifecycle(this.serverManager.getWebServer()));
getBeanFactory().registerSingleton("webServerStartStop",
new WebServerStartStopLifecycle(this.serverManager));
createWebServer.end();
}
initPropertySources();
}
评论区