SpringBoot源码篇:Spring5内置tomcat实现code-based的web.xml实现
一、简介
上篇文章讲了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的源码阅读之旅。