欢迎访问shiker.tech

请允许在我们的网站上展示广告

您似乎使用了广告拦截器,请关闭广告拦截器。我们的网站依靠广告获取资金。

Spring boot如何简化项目搭建
(last modified May 6, 2023, 7:37 PM )
by
侧边栏壁纸
  • 累计撰写 176 篇文章
  • 累计创建 61 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

Spring boot如何简化项目搭建

橙序员
2023-05-06 / 1 评论 / 2 点赞 / 583 阅读 / 1,437 字 / 正在检测百度是否收录... 正在检测必应是否收录...
文章摘要(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且不包含DispatcherServletServletContainer则为云原生应用,如果我们引用类不包含ServletConfigurableWebApplicationContext则为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环境的服务容器

image-20230506190857677

这三个容器的主要创建流程均为:

  1. 用户调用启动函数
  2. 获取spring boot默认配置
  3. 获取程序启动变量,读取配置文件
  4. 根据应用类型创建应用容器
  5. 容器初始化
  6. 配置容器属性
  7. 容器刷新
  8. 自定义刷新:创建应用内嵌服务器
  9. 启动服务器,并监听容器完成事件,完成应用启动

spring-mvc-web应用启动流程

java应用所需容器很简单,这里我们不多做赘述。我们重点来看下后两个的创建流程。

WEB容器

在web容器的自定义刷新方法中,spring boot主要做了两件事:

  1. 内嵌web服务器的配置
  2. 获取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();
	}

2

评论区