SpringBoot源码篇:Spring5内置tomcat实现code-based的web.xml实现

Java框架

浏览数:77

2019-9-17

AD:资源代下载服务

一、简介

  上篇文章讲了SpingBoot诞生的历史背景和技术演进背景,并通过源码说明了SpringBoot是如何实现零配置的包括如何省去web.xml配置的原理。本文接上一篇文章,通过demo演示SpringBoot是如何内置tomcat并实现基于java配置的Servlet初始化和SpringBoot的启动流程。

二、基于java配置的web.xml实现

传统SpringMVC框架web.xml的配置内容

 1 <web-app>
 2     <!-- 初始化Spring上下文 -->
 3     <listener>
 4         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 5     </listener>
 6     <!-- 指定Spring的配置文件 -->
 7     <context-param>
 8         <param-name>contextConfigLocation</param-name>
 9         <param-value>/WEB-INF/app-context.xml</param-value>
10     </context-param>
11     <!-- 初始化DispatcherServlet -->
12     <servlet>
13         <servlet-name>app</servlet-name>
14         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
15         <init-param>
16             <param-name>contextConfigLocation</param-name>
17             <param-value></param-value>
18         </init-param>
19         <load-on-startup>1</load-on-startup>
20     </servlet>
21     <servlet-mapping>
22         <servlet-name>app</servlet-name>
23         <url-pattern>/app/*</url-pattern>
24     </servlet-mapping>
25 </web-app>

查看Spring官方文档https://docs.spring.io/spring/docs/5.0.14.RELEASE/spring-framework-reference/web.html#spring-web

文档中给出了如何使用java代码实现web.xml配置的example

 1 public class MyWebApplicationInitializer implements WebApplicationInitializer {
 2 
 3     @Override
 4     public void onStartup(ServletContext servletCxt) {
 5 
 6         // Load Spring web application configuration
 7         //通过注解的方式初始化Spring的上下文
 8         AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
 9         //注册spring的配置类(替代传统项目中xml的configuration)
10         ac.register(AppConfig.class);
11         ac.refresh();
12 
13         // Create and register the DispatcherServlet
14         //基于java代码的方式初始化DispatcherServlet
15         DispatcherServlet servlet = new DispatcherServlet(ac);
16         ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
17         registration.setLoadOnStartup(1);
18         registration.addMapping("/app/*");
19     }
20 }

通过example可见基于java的web.xml的实现

三、代码实现简易版SpringBoot

 1、工程目录结构

2、pom.xml依赖

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 4     <modelVersion>4.0.0</modelVersion>
 5     <groupId>com.shf</groupId>
 6     <artifactId>spring-tomcat</artifactId>
 7     <version>0.0.1-SNAPSHOT</version>
 8     <name>spring-tomcat</name>
 9     <description>Demo project for Spring Boot</description>
10 
11     <properties>
12         <java.version>1.8</java.version>
13     </properties>
14 
15     <dependencies>
16         <dependency>
17             <groupId>org.springframework</groupId>
18             <artifactId>spring-web</artifactId>
19             <version>5.0.8.RELEASE</version>
20         </dependency>
21         <dependency>
22             <groupId>org.apache.tomcat.embed</groupId>
23             <artifactId>tomcat-embed-core</artifactId>
24             <version>8.5.32</version>
25         </dependency>
26         <dependency>
27             <groupId>org.springframework</groupId>
28             <artifactId>spring-context</artifactId>
29             <version>5.0.8.RELEASE</version>
30         </dependency>
31         <dependency>
32             <groupId>org.springframework</groupId>
33             <artifactId>spring-webmvc</artifactId>
34             <version>5.0.8.RELEASE</version>
35         </dependency>
36     </dependencies>
37 
38     <build>
39         <plugins>
40             <plugin>
41                 <groupId>org.springframework.boot</groupId>
42                 <artifactId>spring-boot-maven-plugin</artifactId>
43             </plugin>
44         </plugins>
45     </build>
46 
47 </project>

3、初始化tomcat实例

 1 package com.shf.tomcat.application;
 2 
 3 
 4 import org.apache.catalina.LifecycleException;
 5 import org.apache.catalina.startup.Tomcat;
 6 
 7 import javax.servlet.ServletException;
 8 
 9 /**
10  * 描述:初始化tomcat
11  *
12  * @Author shf
13  * @Date 2019/5/26 14:58
14  * @Version V1.0
15  **/
16 public class SpringApplication {
17     public static void run(){
18         //创建tomcat实例
19         Tomcat tomcat = new Tomcat();
20         //设置tomcat端口
21         tomcat.setPort(8000);
22         try {
23             //此处随便指定一下webapp,让tomcat知道这是一个web工程
24             tomcat.addWebapp("/", "D:\\");
25             //启动tomcat
26             tomcat.start();
27             tomcat.getServer().await();
28         } catch (LifecycleException e) {
29             e.printStackTrace();
30         } catch (ServletException e) {
31             e.printStackTrace();
32         }
33     }
34 }

4、AppConfig.java

该类主要实现Spring的配置,基于java实现spring xml的配置

 1 package com.shf.tomcat.web;
 2 
 3 import org.springframework.context.annotation.Bean;
 4 import org.springframework.context.annotation.ComponentScan;
 5 import org.springframework.context.annotation.Configuration;
 6 
 7 import javax.servlet.http.HttpServlet;
 8 
 9 /**
10  * 描述:java代码实现类似于spring-context.xml的配置
11  *
12  * @Author shf
13  * @Date 2019/5/22 21:28
14  * @Version V1.0
15  **/
16 @Configuration
17 @ComponentScan("com.shf.tomcat")
18 public class AppConfig extends HttpServlet {
19     @Bean
20     public String string(){
21         return new String("hello");
22     }
23 }

5、MyWebApplicationInitializer.java

 值得一说,该类就是基于java的web.xml的配置

 1 package com.shf.tomcat.web;
 2 
 3 import org.springframework.web.WebApplicationInitializer;
 4 import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
 5 import org.springframework.web.servlet.DispatcherServlet;
 6 
 7 import javax.servlet.ServletContext;
 8 import javax.servlet.ServletException;
 9 import javax.servlet.ServletRegistration;
10 
11 /**
12  * 描述:WebApplicationInitializer实现web.xml的配置
13  *
14  * @Author shf
15  * @Date 2019/5/22 21:25
16  * @Version V1.0
17  **/
18 public class MyWebApplicationInitializer implements WebApplicationInitializer {
19     public void onStartup(ServletContext servletContext) throws ServletException {
20         System.out.println("初始化 MyWebApplicationInitializer");
21         //通过注解的方式初始化Spring的上下文
22         AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
23         //注册spring的配置类(替代传统项目中xml的configuration)
24         ac.register(AppConfig.class);
25 //        ac.refresh();
26 
27         // Create and register the DispatcherServlet
28         //基于java代码的方式初始化DispatcherServlet
29         DispatcherServlet servlet = new DispatcherServlet(ac);
30         ServletRegistration.Dynamic registration = servletContext.addServlet("/", servlet);
31         registration.setLoadOnStartup(1);
32         registration.addMapping("/*");
33     }
34 }

6、MySpringServletContainerInitializer.java

该类上篇文章已经讲的很清楚了

 1 package com.shf.tomcat.web;
 2 
 3 import org.springframework.core.annotation.AnnotationAwareOrderComparator;
 4 import org.springframework.util.ReflectionUtils;
 5 import org.springframework.web.WebApplicationInitializer;
 6 
 7 import javax.servlet.ServletContainerInitializer;
 8 import javax.servlet.ServletContext;
 9 import javax.servlet.ServletException;
10 import javax.servlet.annotation.HandlesTypes;
11 import java.lang.reflect.Modifier;
12 import java.util.LinkedList;
13 import java.util.List;
14 import java.util.Set;
15 
16 @HandlesTypes(MyWebApplicationInitializer.class)
17 public class MySpringServletContainerInitializer implements ServletContainerInitializer {
18     public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
19         List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();
20 
21         if (webAppInitializerClasses != null) {
22             for (Class<?> waiClass : webAppInitializerClasses) {
23                 if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
24                         WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
25                     try {
26                         initializers.add((WebApplicationInitializer)
27                                 ReflectionUtils.accessibleConstructor(waiClass).newInstance());
28                     }
29                     catch (Throwable ex) {
30                         throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
31                     }
32                 }
33             }
34         }
35 
36         if (initializers.isEmpty()) {
37             servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
38             return;
39         }
40 
41         servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
42         AnnotationAwareOrderComparator.sort(initializers);
43         for (WebApplicationInitializer initializer : initializers) {
44             initializer.onStartup(servletContext);
45         }
46     }
47 }

7、META-INF/services/javax.servlet.ServletContainerInitializer

在该文件中配置ServletContainerInitializer的实现类

 

8、测试类

写一个测试类

 1 package com.shf.tomcat.controller;
 2 
 3 import org.springframework.web.bind.annotation.RequestMapping;
 4 import org.springframework.web.bind.annotation.RestController;
 5 
 6 @RestController
 7 public class TestController {
 8     @RequestMapping("/app/test")
 9     public String test(){
10         System.out.println("--- hello ---");
11         return "hello";
12     }
13 }

 9、主类

 1 package com.shf.tomcat;
 2 
 3 
 4 import com.shf.tomcat.application.SpringApplication;
 5 
 6 public class Main {
 7 
 8     public static void main(String[] args) {
 9         SpringApplication.run();
10     }
11 
12 }

10、测试

启动Main方法

浏览器访问:http://localhost:8080/app/test

四、小结

  上篇文章介绍了SpringBoot是如何实现的基于java配置的web.xml。这篇文章我们通过一个demo来认识SpringBoot就是是如何内置tomcat并且实现零配置的。其实这个demo就像是一个简易版的SpringBoot的框架,基本模拟了SpringBoot的启动流程,只是差了SpringBoot最重要的能力—自动装配。

  这两篇文章严格来说不应该算是SpringBoot的源码篇,但是笔者认为关于SpringBoot的发展历史、技术演进路线、及SpringBoot的嵌入式tomcat和code-based web.xml配置也是认识SpringBoot重要的一部分。

  下一篇文章正式开始SpringBoot的源码阅读之旅。

 

作者:超级小小黑