注:
本文需要一定的Spring及Linux/unix操作基础。
本文所有源代码均可在https://github.com/favorstack/spring-boot-demo下载。

一 简介

随着微服务的盛行,Spring Boot几乎成为了Java界的标配。从Spring官网首页也可以看出其地位,并且在其项目列表中已跃升至第一位。

Spring Boot的出现并非用于替代Spring Framework,相反,它用于简化Spring的开发,将Spring中的“约定优于配置”发挥到了极致,其核心仍然是Spring Framework,并将各种第三方框架与Spring平台完美地整合到了一起,目的一如当初创建Spring时一样,简化Java企业开发,让我们更容易的创建独立的,可用于生产级的Spring应用。换句话说,Spring Boot可以让我们更专注于业务。

目前Spring Boot的迭代依然很快,当前的GA版本为2.1.6,本文及后后续均以该版本为基础。

二 系统要求

Spring Boot 2.1.6 要求Java8+,兼容Java11;并且要求Spring Framework5.1.8+。
构建工具要求:

构建工具版本
Maven3.3+
Gradle4.4+

支持以下内嵌Servlet容器:

NameServlet Version
Tomcat 9.04.0
Jetty 9.43.1
Undertow 2.04.0
也可以将Spring Boot应用程序部署到任何兼容Servlet 3.1+的容器

三 安装配置Spring Boot开发环境

Spring Boot提供了多种不同的安装方式,有针对刚接触Java开发的Spring Boot CLI (Command Line Interface),也有经典的Java应用安装方式。我们将采用第二种。

3.1 检查Java版本

由于Spring Boot是Java应用,所以首先需要配置Java环境,可以参考这一篇文章。

安装完毕执行以下命令检查:

$ java -version

3.2 检查Maven版本

本文将使用Maven作为构建管理工具,关于Maven的安装和配置,请参考这里

安装完毕执行以下命令检查:

$ mvn -v

一个基本的Spring Boot maven pom文件如下:

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>io.favorstack</groupId>
    <artifactId>myfirstboot-app</artifactId>
    <version>1.0.0-SNAPSHOT</version>

    <!-- 从Spring Boot继承默认配置 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>

    <!-- 为web应用添加依赖 -->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <!-- 打包为可执行jar文件 -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

四 第一个Spring Boot应用

首先,在任意目录下创建一个目录,例如:myfirstboot-app,然后在该目录下创建一个pom文件:

$ mkdir myfirstboot-app && touch myfirstboot-app/pom.xml

4.1 引入SpringBoot Starter默认配置

编辑刚才创建的pom文件并加入以下内容:

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>io.favorstack</groupId>
    <artifactId>myfirstboot-app</artifactId>
    <version>1.0.0-SNAPSHOT</version>

    <!-- 从Spring Boot继承默认配置 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>

    <!-- 其他内容 -->
    
</project>

4.2 打包

现在我们可以在myfirstboot-app目录下执行mvn clean package命令来构建一下:

$ cd myfirstboot-app && mvn clean package

[INFO] Scanning for projects...
[INFO]
[INFO] -------------------< io.favorstack:myfirstboot-app >--------------------
[INFO] Building myfirstboot-app 1.0.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:3.1.0:clean (default-clean) @ myfirstboot-app ---
[INFO] Deleting /Users/lin/tmp/bootdemo/myfirstboot-app/target
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:resources (default-resources) @ myfirstboot-app ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /Users/lin/tmp/bootdemo/myfirstboot-app/src/main/resources
[INFO] skip non existing resourceDirectory /Users/lin/tmp/bootdemo/myfirstboot-app/src/main/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ myfirstboot-app ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /Users/lin/tmp/bootdemo/myfirstboot-app/target/classes
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:testResources (default-testResources) @ myfirstboot-app ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /Users/lin/tmp/bootdemo/myfirstboot-app/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ myfirstboot-app ---
[INFO] No sources to compile
[INFO]
[INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ myfirstboot-app ---
[INFO] No tests to run.
[INFO]
[INFO] --- maven-jar-plugin:3.1.2:jar (default-jar) @ myfirstboot-app ---
[WARNING] JAR will be empty - no content was marked for inclusion!
[INFO] Building jar: /Users/lin/tmp/bootdemo/myfirstboot-app/target/myfirstboot-app-1.0.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.421 s
[INFO] Finished at: 2019-07-17T12:19:05+08:00
[INFO] ------------------------------------------------------------------------

构建成功,不过我们发现会有一个警告,提示jar包为空:[WARNING] JAR will be empty - no content was marked for inclusion!,现在先不用管它,因为我们尚未用到任何依赖。

spring-boot-starter-parent 可以让接下来的其他boot依赖省掉版本标签。例如下面的web依赖无需再指定版本。

可以使用mvn dependency:tree来分析当前pom中的所有依赖:

$ mvn dependency:tree

[INFO] Scanning for projects...
[INFO]
[INFO] -------------------< io.favorstack:myfirstboot-app >--------------------
[INFO] Building myfirstboot-app 1.0.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:3.1.1:tree (default-cli) @ myfirstboot-app ---
[INFO] io.favorstack:myfirstboot-app:jar:1.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.487 s
[INFO] Finished at: 2019-07-17T12:44:58+08:00
[INFO] ------------------------------------------------------------------------

4.3 添加一个web应用依赖

Spring Boot提供了许多开发特定应用的“Starters”依赖项,比如,我们要开发一个web应用,可以在pom中加入以下web依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

现在,再次执行mvn dependency:tree,我们会发现多了许多依赖:

$ mvn dependency:tree

[INFO] Scanning for projects...
[INFO]
[INFO] -------------------< io.favorstack:myfirstboot-app >--------------------
[INFO] Building myfirstboot-app 1.0.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:3.1.1:tree (default-cli) @ myfirstboot-app ---
[INFO] io.favorstack:myfirstboot-app:jar:1.0.0-SNAPSHOT
[INFO] \- org.springframework.boot:spring-boot-starter-web:jar:2.1.6.RELEASE:compile
[INFO]    +- org.springframework.boot:spring-boot-starter:jar:2.1.6.RELEASE:compile
[INFO]    |  +- org.springframework.boot:spring-boot:jar:2.1.6.RELEASE:compile
[INFO]    |  +- org.springframework.boot:spring-boot-autoconfigure:jar:2.1.6.RELEASE:compile
[INFO]    |  +- org.springframework.boot:spring-boot-starter-logging:jar:2.1.6.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.8.RELEASE:compile
[INFO]    |  |  \- org.springframework:spring-jcl:jar:5.1.8.RELEASE:compile
[INFO]    |  \- org.yaml:snakeyaml:jar:1.23:runtime
[INFO]    +- org.springframework.boot:spring-boot-starter-json:jar:2.1.6.RELEASE:compile
[INFO]    |  +- com.fasterxml.jackson.core:jackson-databind:jar:2.9.9:compile
[INFO]    |  |  +- com.fasterxml.jackson.core:jackson-annotations:jar:2.9.0:compile
[INFO]    |  |  \- com.fasterxml.jackson.core:jackson-core:jar:2.9.9:compile
[INFO]    |  +- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:jar:2.9.9:compile
[INFO]    |  +- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:jar:2.9.9:compile
[INFO]    |  \- com.fasterxml.jackson.module:jackson-module-parameter-names:jar:2.9.9:compile
[INFO]    +- org.springframework.boot:spring-boot-starter-tomcat:jar:2.1.6.RELEASE:compile
[INFO]    |  +- org.apache.tomcat.embed:tomcat-embed-core:jar:9.0.21:compile
[INFO]    |  +- org.apache.tomcat.embed:tomcat-embed-el:jar:9.0.21:compile
[INFO]    |  \- org.apache.tomcat.embed:tomcat-embed-websocket:jar:9.0.21:compile
[INFO]    +- org.hibernate.validator:hibernate-validator:jar:6.0.17.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.8.RELEASE:compile
[INFO]    |  \- org.springframework:spring-beans:jar:5.1.8.RELEASE:compile
[INFO]    \- org.springframework:spring-webmvc:jar:5.1.8.RELEASE:compile
[INFO]       +- org.springframework:spring-aop:jar:5.1.8.RELEASE:compile
[INFO]       +- org.springframework:spring-context:jar:5.1.8.RELEASE:compile
[INFO]       \- org.springframework:spring-expression:jar:5.1.8.RELEASE:compile
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.938 s
[INFO] Finished at: 2019-07-17T12:50:12+08:00
[INFO] ------------------------------------------------------------------------
主要有spring-boot-starter-web,spring-boot-starter-json,spring-boot-starter-tomcat,hibernate-validator,spring-web,spring-webmvc等。

4.4 Hello world示例代码

由于我们使用Maven构建管理我们的应用,所以源代码的目录结构需要遵循Maven风格的目录结构:

1). 首先在pom文件同级下创建src/main/java目录,并创建一个Example.java文件:

$ mkdir -p src/main/java && touch src/main/java/Example.java

2). 然后在刚才创建的Example.java文件中编写以下代码:

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 Example {

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

    public static void main(String[] args) {
        SpringApplication.run(Example.class, args);
    }
}

3). 运行:
直接执行mvn spring-boot:run命令即可启动,启动完毕,可以直接在浏览器中访问localhost:8080,没问题的话,应该能看到如下内容了:

Hello world!
run定义于spring-boot-starter-parent这个pom

4). 停止:
直接在终端中按ctrl+c即可。

4.4.1 @EnableAutoConfiguration注解

@EnableAutoConfiguration注解可以让Spring Boot根据我们所添加的jar依赖来猜测我们想如何配置Spring应用上下文。例如我们添加了spring-boot-starter-web之后,会将Tomcat和SpringMVC的相关依赖一块引入,它会因此推测出我们是在开发一个web应用,并自动按web应用需要的配置来配置好,我们无需再去配置要扫描哪些包,哪些配置文件之类,当然,配置都是按默认值来的,比如Tomcat端口8080,这也体现了约定优于配置的思想,除非你想要更改默认配置,否则无需关注。

另外还有一个注解@SpringBootApplication,该注解等价于同时声明@Configuration,@EnableAutoConfiguration,@ComponentScan这三个注解。
通常推荐将@EnableAutoConfiguration注解放到根包中(如果没有使用@SpringBootApplication的话),这样所有的子包和类都可以被搜索到。

4.4.2 main方法

该方法就是一个Java应用程序的标准入口main方法。该方法通过调用run方法来将执行权委托给Spring Boot的SpringApplication类,最终由SpringApplication类来引导我们的应用,并在此过程中完成spring及Tomcat容器的配置和启动。另外,该类的run方法需要两个参数,第一个为一个类,告知SpringApplication哪个类是要加载的主要的Spring组件;第二个为命令行参数列表。

4.5 生成可执行jar包

与普通的jar包不同的是,可执行jar包中会包含代码运行所需要的所有依赖,这种jar包通常体积很大(内嵌了许多其他jar包),所以又被称为fat-jar(这个词很形象)。

更多关于Spring Boot可执行jar包的内容可以参考executable-jar

1). 首先在pom加入依赖:

<build>
    <plugins>
        <!-- 打包为可执行jar文件 -->
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>
spring-boot-starter-parent 的POM中包含了<executions>配置,绑定了repackage目标。如果不使用该parent POM,那么需要自己手动声明该配置,详见 plugin documentation

2). 打包:

$ mvn clean package

[INFO] Scanning for projects...
[INFO]
[INFO] -------------------< io.favorstack:myfirstboot-app >--------------------
[INFO] Building myfirstboot-app 1.0.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:3.1.0:clean (default-clean) @ myfirstboot-app ---
[INFO] Deleting /Users/lin/tmp/bootdemo/myfirstboot-app/target
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:resources (default-resources) @ myfirstboot-app ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /Users/lin/tmp/bootdemo/myfirstboot-app/src/main/resources
[INFO] skip non existing resourceDirectory /Users/lin/tmp/bootdemo/myfirstboot-app/src/main/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ myfirstboot-app ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /Users/lin/tmp/bootdemo/myfirstboot-app/target/classes
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:testResources (default-testResources) @ myfirstboot-app ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /Users/lin/tmp/bootdemo/myfirstboot-app/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ myfirstboot-app ---
[INFO] No sources to compile
[INFO]
[INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ myfirstboot-app ---
[INFO] No tests to run.
[INFO]
[INFO] --- maven-jar-plugin:3.1.2:jar (default-jar) @ myfirstboot-app ---
[INFO] Building jar: /Users/lin/tmp/bootdemo/myfirstboot-app/target/myfirstboot-app-1.0.0-SNAPSHOT.jar
[INFO]
[INFO] --- spring-boot-maven-plugin:2.1.6.RELEASE:repackage (repackage) @ myfirstboot-app ---
[INFO] Replacing main artifact with repackaged archive
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 5.529 s
[INFO] Finished at: 2019-07-17T13:00:19+08:00
[INFO] ------------------------------------------------------------------------

3). 运行程序:

java -jar target/myfirstboot-app-1.0.0-SNAPSHOT.jar

4). 停止:
同样直接在终端中按ctrl+c即可。

5). 如果查看target目录下的编译打包结果会发现有两个jar包:

$ ll target/

total 32808
drwxr-xr-x  8 lin  staff       256  7 17 13:16 ./
drwxr-xr-x  5 lin  staff       160  7 17 13:16 ../
drwxr-xr-x  4 lin  staff       128  7 17 13:16 classes/
drwxr-xr-x  3 lin  staff        96  7 17 13:16 generated-sources/
drwxr-xr-x  3 lin  staff        96  7 17 13:16 maven-archiver/
drwxr-xr-x  3 lin  staff        96  7 17 13:16 maven-status/
-rw-r--r--  1 lin  staff  16791006  7 17 13:16 myfirstboot-app-1.0.0-SNAPSHOT.jar
-rw-r--r--  1 lin  staff      2472  7 17 13:16 myfirstboot-app-1.0.0-SNAPSHOT.jar.original

其中体积较大的myfirstboot-app-1.0.0-SNAPSHOT.jar为Spring Boot插件重新打包的jar,这个即为可执行jar包;另一个myfirstboot-app-1.0.0-SNAPSHOT.jar.original是Maven打包的原始jar,只包含我们自己编写的代码,体积较小,不能单独运行。

可以使用jar tvf target/myfirstboot-app-1.0.0-SNAPSHOT.jar命令查看jar包的内部结构。

4.6 注意事项

需要注意的是,原本我们开发的是一个Java web应用,但似乎这里的可执行jar包并没有接触到Servlet容器相关的东西,千万不要以为这是Spring Boot提供了该运行时环境的能力,只不过是Spring Boot默认把Servlet容器引入进来给我们简化掉这一操作罢了,即便不使用Spring Boot,你也同样可以编写这种内嵌Servlet容器形式的web应用,如嵌入Tomcat,Jetty等,但这要额外写更多的代码。

五 Spring Initializr

最后,Spring Boot还提供了一个Initializr工具,用于生成Spring Boot基本的项目结构。可以直接访问https://start.spring.io/来打开该页面:
getting-started-01.jpg
根据需要选择好各个选项后,点击下边的Cenerate the project按钮即可下载打包好的项目结构。

不管是之前我们手动创建的项目结构,还是用Initalizr导出的,此时都可以导入你喜欢的IDE中继续开发,当然,一些IDE也提供了直接初始化Spring Boot项目的功能,例如Idea:
getting-started-02.jpg

参考:
https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/html/getting-started-system-requirements.html
https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/html/getting-started-installing-spring-boot.html
https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/html/getting-started-first-application.html