本篇文章是SpringBoot最入门的介绍。咱们不借助任何额外的工具,从无到有建立一个Spring Boot的web项目,并运行这个项目。java
归根结底,Spring Boot就只是一个框架,几个jar而已,没什么神奇的。但使用Spring Initializr建立项目的过程把不少信息屏蔽掉了,这样咱们就很难搞清楚Spring Boot的本质是什么。下面仅使用maven从无到有构建一个Spring Boot的web项目。
先建立一个maven空工程以下所示,项目的名字叫spring-boot-hello。git
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.poype</groupId> <artifactId>spring-boot-hello</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> </dependencies> </project>
如今这仍是一个空的maven项目,咱们能够在dependencies标签中添加咱们须要的依赖,例如添加Spring Boot的依赖。可是Spring Boot为了减小配置,方便咱们开发,提供了一个parent maven工程spring-boot-starter-parent,咱们只要让咱们的这个项目继承spring-boot-starter-parent工程,就能减小好多配置。修改咱们的POM配置以下:github
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.poype</groupId> <artifactId>spring-boot-hello</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.4.RELEASE</version> </parent> <dependencies> </dependencies> </project>
目前咱们的这个maven项目尚未导入任何dependency,这点能够经过执行mvn dependency:tree
命令肯定。
咱们要建立的是一个web项目,因此添加spring-boot-starter-web这个依赖。修改POM配置以下:web
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.poype</groupId> <artifactId>spring-boot-hello</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.4.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> </project>
因为在spring-boot-starter-parent的dependencyManagement
中已经用声明了spring-boot-starter-web,因此此处咱们能够省略它的version配置。
再次执行mvn dependency:tree
命令得到以下结果:spring
[INFO] Scanning for projects... [INFO] [INFO] --------------------< com.poype:spring-boot-hello >--------------------- [INFO] Building spring-boot-hello 1.0-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- maven-dependency-plugin:3.1.1:tree (default-cli) @ spring-boot-hello --- [INFO] com.poype:spring-boot-hello:jar:1.0-SNAPSHOT [INFO] \- org.springframework.boot:spring-boot-starter-web:jar:2.1.4.RELEASE:compile [INFO] +- org.springframework.boot:spring-boot-starter:jar:2.1.4.RELEASE:compile [INFO] | +- org.springframework.boot:spring-boot:jar:2.1.4.RELEASE:compile [INFO] | +- org.springframework.boot:spring-boot-autoconfigure:jar:2.1.4.RELEASE:compile [INFO] | +- org.springframework.boot:spring-boot-starter-logging:jar:2.1.4.RELEASE:compile [INFO] | | +- ch.qos.logback:logback-classic:jar:1.2.3:compile [INFO] | | | +- ch.qos.logback:logback-core:jar:1.2.3:compile [INFO] | | | \- org.slf4j:slf4j-api:jar:1.7.26:compile [INFO] | | +- org.apache.logging.log4j:log4j-to-slf4j:jar:2.11.2:compile [INFO] | | | \- org.apache.logging.log4j:log4j-api:jar:2.11.2:compile [INFO] | | \- org.slf4j:jul-to-slf4j:jar:1.7.26:compile [INFO] | +- javax.annotation:javax.annotation-api:jar:1.3.2:compile [INFO] | +- org.springframework:spring-core:jar:5.1.6.RELEASE:compile [INFO] | | \- org.springframework:spring-jcl:jar:5.1.6.RELEASE:compile [INFO] | \- org.yaml:snakeyaml:jar:1.23:runtime [INFO] +- org.springframework.boot:spring-boot-starter-json:jar:2.1.4.RELEASE:compile [INFO] | +- com.fasterxml.jackson.core:jackson-databind:jar:2.9.8:compile [INFO] | | +- com.fasterxml.jackson.core:jackson-annotations:jar:2.9.0:compile [INFO] | | \- com.fasterxml.jackson.core:jackson-core:jar:2.9.8:compile [INFO] | +- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:jar:2.9.8:compile [INFO] | +- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:jar:2.9.8:compile [INFO] | \- com.fasterxml.jackson.module:jackson-module-parameter-names:jar:2.9.8:compile [INFO] +- org.springframework.boot:spring-boot-starter-tomcat:jar:2.1.4.RELEASE:compile [INFO] | +- org.apache.tomcat.embed:tomcat-embed-core:jar:9.0.17:compile [INFO] | +- org.apache.tomcat.embed:tomcat-embed-el:jar:9.0.17:compile [INFO] | \- org.apache.tomcat.embed:tomcat-embed-websocket:jar:9.0.17:compile [INFO] +- org.hibernate.validator:hibernate-validator:jar:6.0.16.Final:compile [INFO] | +- javax.validation:validation-api:jar:2.0.1.Final:compile [INFO] | +- org.jboss.logging:jboss-logging:jar:3.3.2.Final:compile [INFO] | \- com.fasterxml:classmate:jar:1.4.0:compile [INFO] +- org.springframework:spring-web:jar:5.1.6.RELEASE:compile [INFO] | \- org.springframework:spring-beans:jar:5.1.6.RELEASE:compile [INFO] \- org.springframework:spring-webmvc:jar:5.1.6.RELEASE:compile [INFO] +- org.springframework:spring-aop:jar:5.1.6.RELEASE:compile [INFO] +- org.springframework:spring-context:jar:5.1.6.RELEASE:compile [INFO] \- org.springframework:spring-expression:jar:5.1.6.RELEASE:compile [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2.018 s [INFO] Finished at: 2019-05-19T22:54:50+08:00 [INFO] ------------------------------------------------------------------------
能够看到在添加spring-boot-starter-web这个依赖后,有许多的jar都被导入了。
除了更多的spring-boot-starter-*被导入了以外,更重要的是不少Spring Framework的jar也被导入了,包括spring-core、spring-beans、spring-context、spring-aop等等。另外还有与tomcat相关的jar也被导入了,也就是说如今咱们已经有了能够运行web程序的servlet容器了。
工程配置已经完成,新建一个HelloController测试类,输入以下代码:express
package com.poype.springboot.web; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @EnableAutoConfiguration public class HelloController { @RequestMapping("/hello") String home() { return "Hello World!"; } public static void main(String[] args) { SpringApplication.run(HelloController.class, args); } }
如今,咱们已经完成了一个简单的web应用开发,能够启动咱们这个应用了。
因为咱们的工程继承了spring-boot-starter-parent的POM配置,它提供了启动spring-boot应用的相关插件(该插件的run目标用于启动应用),能够经过执行mvn spring-boot:run
命令启动应用,获得以下运行结果。apache
从运行结果中能够看到,spring-boot启动了Tomcat服务器,并监听在8080端口。下面咱们打开浏览器,输入地址http://localhost:8080/hello
就能够看到程序运行结果。json
应用构建好以后,须要build出一个应用包才能用于生产部署。为此须要在POM配置中新增一个插件,修改POM配置以下:api
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.poype</groupId> <artifactId>spring-boot-hello</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.4.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
接着咱们执行mvn clean package
命令,能够在target目录下发现构建好的应用包 spring-boot-hello-1.0-SNAPSHOT.jar。
执行java -jar ./target/spring-boot-hello-1.0-SNAPSHOT.jar
命令就能够启动应用了。浏览器
上面的例子虽然很是简单,但却也是一个标准的spring web应用。咱们能够回忆一下,若是没有Spring Boot,建立一个这样的web应用都须要哪些步骤呢?首先要在maven的POM中导入N多相关dependency(包括Spring的、servlet的、json的...)。而后添加各类复杂配置(包括servlet的、Spring的、Spring MVC的...)。最后写完代码build好一个war包,咱们还须要下载一个Tomcat,并将war包放到tomcat下的指定路径,启动tomcat部署应用。这个过程即便是工做几年的老司机,从无到有建立一个项目估计也要十几分钟,若是是新手再遇到一些问题,解决起来就更麻烦了,可能几个小时也不必定能搞得出来。
使用Spring Boot构建应用,即使咱们仅仅使用maven,也几乎没有什么配置。若是使用Spring Initializr的话,建立好工程无需任何配置就直接能够写代码了,很是的方便,即便是新手几分钟也能搞出来这个HelloWorld应用。这就是Spring Boot给个人最初印象,可是它是如何作到这些的呢?
Spring Boot提供了不少Starter依赖,每种类型的Starter提供了这种类型应用可能须要的一系列dependency(利用maven间接依赖的特性)。例如咱们这里建立的是一个web应用,因此咱们的项目依赖spring-boot-starter-web,而spring-boot-starter-web会将web开发可能须要的依赖所有帮咱们导入,省去不少配置的工做。spring-boot-starter-parent是一个特殊的starter,它提供了许多maven默认配置,如dependenceManagment。
另外一个比较重要的是注解@EnableAutoConfiguration
,Spring Boot看到这个注解,会根据已经加入的jar dependency执行相关的配置。例如在咱们的工程中有Spring MVC和Tomcat的依赖,Spring Boot就会猜到这是一个WEB工程,它就会对项目执行相应的配置(如Spring MVC和Servlet的配置)。
Spring Boot自带了一个Tomcat容器,省去咱们本身安装和配置容器的工做。为了了解Spring Boot的启动过程,咱们将build好的jar解压获得的目录结构以下图所示:
其中最主要的配置文件是MANIFEST.MF,在执行java -jar *.jar启动命令时,JVM参考的就是这个文件的配置。其文件内容以下:
Manifest-Version: 1.0 Implementation-Title: spring-boot-hello Implementation-Version: 1.0-SNAPSHOT Built-By: poype Implementation-Vendor-Id: com.poype Spring-Boot-Version: 2.1.4.RELEASE Main-Class: org.springframework.boot.loader.JarLauncher Start-Class: com.poype.springboot.web.HelloController Spring-Boot-Classes: BOOT-INF/classes/ Spring-Boot-Lib: BOOT-INF/lib/ Created-By: Apache Maven 3.6.1 Build-Jdk: 1.8.0_211 Implementation-URL: https://projects.spring.io/spring-boot/#/spring-bo ot-starter-parent/spring-boot-hello
Main-Class是jar包中的启动类,能够看到是一个叫org.springframework.boot.loader.JarLauncher类,是Spring Boot提供的Launcher类。Start-Class是咱们本身编写的含有main方法的类。JarLauncher的代码以下:
public class JarLauncher extends ExecutableArchiveLauncher { static final String BOOT_INF_CLASSES = "BOOT-INF/classes/"; static final String BOOT_INF_LIB = "BOOT-INF/lib/"; public JarLauncher() {} protected JarLauncher(Archive archive) { super(archive); } @Override protected boolean isNestedArchive(Archive.Entry entry) { if (entry.isDirectory()) { return entry.getName().equals(BOOT_INF_CLASSES); } return entry.getName().startsWith(BOOT_INF_LIB); } public static void main(String[] args) throws Exception { new JarLauncher().launch(args); } }
从代码中能够看出,BOOT-INF/classes/路径下存放了应用程序本身的类,BOOT-INF/lib/路径下存放了第三方依赖的jar,包括内嵌的tomcat jar。
因为这个Jar中既包含应用程序本身的类,又包含应用所依赖的第三方的Jar,还包含Spring Boot Loader相关的类,因此这个Jar被称做Fat Jar。
在JarLauncher类的main函数中,经过launch方法启动整个应用。launch方法的实如今父类Launcher中。在Launcher方法中,会根据MANIFEST.MF中Start-Class的配置项找到咱们本身的Main Class,而后构造一个ClassLoader加载应用类和第三方的Jar,最后会建立一个新的线程执行应用程序本身的main函数。看到这里咱们能够大概总结一下,Spring Boot实现了一套本身的部署路径规范(应用本身的类放在哪里,应用依赖的第三方jar放在哪里等等),就像J2EE规范同样。而后利用tomcat的jar实现servlet容器的功能,对WEB请求进行处理。能够说Spring Boot利用tomcat打造了一个全新的平台,这个平台也仅仅只在servlet容器部分利用到了tomcat的功能,至于部署规范和加载机制,都是Spring Boot本身全新实现的。