利用Spring Boot可以快速的构建一个Spring应用,尤其是Web应用,Spring Boot内嵌了Tomcat容器,项目开发完成后打包成一个可执行的Jar包直接就能运行,大大减少了项目构建和部署的成本。

Spring Boot非常强大,它是如何实现的?

过去的步骤

在没有用Spring Boot之前,是这么做的:

1、在web.xml手动配置Spring配置文件和DispatcherServlet。

<!-- Spring MVC配置 -->
<servlet>
    <servlet-name>spring</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>DispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
  

<!-- Spring配置 -->
<listener>
   <listenerclass>
     org.springframework.web.context.ContextLoaderListener
   </listener-class>
</listener>
  
<!-- 指定Spring Bean的配置文件 -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:config/applicationContext.xml</param-value>
</context-param>

2、将项目打包放到Tomcat容器中运行。

3、Tomcat启动时,解析web.xml,Spring通过ContextLoaderListener来完成上下文环境的初始化。

4、Tomcat将web.xml中配置的前端控制器DispatcherServlet注册到Servlet中。

总结一下启动服务做了哪些事情:

  • 启动了一个Tomcat服务
  • 初始化Spring上下文环境
  • 注册DispatcherServlet

Spring Boot要想达到零配置,这些问题必须要解决。

解决方案

  • 启动Tomcat -> 内嵌解决
  • 初始化Spring上下文环境 -> 利用SPI机制
  • 动态注册DispatcherServlet -> 初始化时一并做掉

模拟实现

1、新建一个maven项目,添加如下依赖:

只是依赖了Spring、MVC和Tomcat,并没有依赖SpringBoot。

<dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.1.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.2.1.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>

    <!--tomcat-->
    <dependency>
      <groupId>org.apache.tomcat.embed</groupId>
      <artifactId>tomcat-embed-core</artifactId>
      <version>8.5.45</version>
    </dependency>
</dependencies>

2、创建Spring初始化上下文环境的配置类。

@Configuration
@ComponentScan("com.ch")
public class AppConfig {

}

3、编写一个测试的Controller,后面测试用。

@RestController
public class MyController {

	@RequestMapping("/test")
	public String test(){
		return "Hello world!";
	}
}

4、创建一个内嵌Tomcat的启动类。

public class TomcatStart {
	public static int TOMCAT_PORT = 8080;
	public static String WEBAPP_PATH = "moni-spring-boot/src/main";
	public static String CONTEXT_PATH = "/";

	public static void run(){
		String s = System.getProperty("user.dir") + File.separator + WEBAPP_PATH;
		System.out.println(s);
		Tomcat tomcat = new Tomcat();
		tomcat.setPort(TOMCAT_PORT);
		//创建上下文
		StandardContext myCtx = (StandardContext) tomcat
				.addWebapp(CONTEXT_PATH, System.getProperty("user.dir") + File.separator + WEBAPP_PATH);
		try {
			tomcat.start();
		} catch (LifecycleException e) {
			e.printStackTrace();
		}
		tomcat.getServer().await();
	}
}

5、创建应用程序的启动类。

实际上就是启动一个Tomcat。

public class App {
	public static void main(String[] args) throws LifecycleException {
		TomcatStart.run();
	}
}

到这里并没有结束,目前只是能启动一个Tomcat,但是Spring上下文环境并没有初始化,DispatcherServlet也还没有注册。

如何让Tomcat启动时完成Spring上下文的初始化和注册filter???

6、编写应用系统初始化类。

/**
 * @Author: pch
 * @Date: 2019/12/14 19:37
 * @Description: App上下文环境初始化
 */
public class MyAppContextInit implements ServletContainerInitializer {
	@Override
	public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
		//初始化Spring上下文环境
		System.out.println("*****SpringContext初始化...*****");
		AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
		context.register(AppConfig.class);
		context.refresh();
		System.out.println("*****SpringContext初始化完成.*****");

		//注册DispatcherServlet
		DispatcherServlet servlet = new DispatcherServlet(context);
		ServletRegistration.Dynamic registration = servletContext.addServlet("myApp", servlet);
		registration.setLoadOnStartup(1);
		registration.addMapping("/");
	}
}

这样还不够,只是编写了这个类,Tomcat凭什么启动时去执行它???

这设计到Java的SPI机制,具体可以看笔者以前的博客:Java SPI机制

7、在resources目录下创建以下文件,文件内容为应用初始化类的全限定名:

META-INF/services/javax.servlet.ServletContainerInitializer

com.ch.support.MyAppContextInit

现在运行main方法,会惊奇的发现,Tomcat启动时会自动执行MyAppContextInit中的onStartup()方法,Spring上下文环境初始完毕,DispatcherServlet也成功注册。

8、测试服务是否正常可用。

访问:http://localhost:8080/test

返回如下:

Hello world!

服务成功可用,基于零配置了实现了类似Spring Boot的基本功能。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐