Spring Boot相信在接触Spring的过程中听到了很多次,其实就是一个用来简化Spring应用开发的初始搭建和开发过程,在之前我们无论是使用Spring还是Spring MVC或者Spring Security,总要先进行各种各样的配置,而且很多配置也依然是模板化的。

约定大于配置逐渐成为了开发者的共识,Spring 4就从XML配置转向各种注解,但是相比很多脚本语言和其他后端,Spring的开发方式略显笨重。

Spring Boot是伴随着Spring 4一同发布的一个产品,也是基于Spring框架的IOC容器。从根本上讲,Spring就是一些库的有机集合,是Spring的一个封装,所有用Spring做的事情,也可以用Spring Boot来做,很好的对应了现在的微服务的开发趋势。

Spring Boot的官网是https://spring.io/projects/spring-boot,可以找到详细的文档。

有了Spring的基础,学习的目标就是通过Spring Boot来创建一个完整的增删改查应用。

要创建一个Spring应用,要考虑的事情还真不少,比如:

  1. 选用哪种Maven原型
  2. 使用哪些依赖
  3. 使用哪种形式配置Spring,XML还是Java呢?
  4. 使用哪种HTTP服务,Tomcat还是JBOSS还是其他?

即使选择好了,也只是个开始,还要写几百行的配置,依赖,很多还是样板代码,比如前段转发器,视图解析器,数据库连接。如果配置写错了,还不太容易发现问题。

Spring Boot的出现就为了解决这些问题,可以减小手工配置的工作量,通过props文件或者classpath自动配置,解决依赖冲突,甚至还集成HTTP服务,一启动直接就可以访问。

https://start.spring.io/提供了一个初始化工具,用于快速的创建一个Spring Boot项目。

Spring Boot可以将项目打包成一个jar文件,直接运行就是一个Web服务,还提供了命令行工具,用于直接运行项目,也可以打包成War文件用于传统的部署方式。

Spring Boot 第一个简单项目

访问https://start.spring.io/,可以看到里边有很多选项:

  1. Project,是项目构建类型,可以选择是Maven项目还是Grandle项目
  2. Language,是采用的语言,可以选择Java,Kotlin和Groovy
  3. Spring Boot,是采用的Spring Boot的版本,目前稳定的大版本是最后的不带SNAPSHOT的版本2.1.3和1.5.19
  4. Project Metadata,是项目的源信息,就是Maven项目里的那些网站名,项目名称等。
  5. Packaging,这个配置需要展开More Options,Jar表示独立的Jar包,而War用于传统部署方式。
  6. Java version,8或者11。
  7. Dependencies,项目依赖,这个很重要,可以选下边的See all查看所有的依赖类型。有很多不同类型的应用可以开发。

下边就来创建一个Web项目,选择Maven项目,Java语言,2.1.3版本的Spring Boot,Group里填写cc.conyli.springbootdemo,Artifact中填写mydemo,选择Jar包和Java 8版本。

之后在项目依赖中选择Web大类下边的第一个Web,包含Tomcat服务器和Spring MVC。

然后点击最下边的Generate Project - alt + ⏎按钮,会下载一个和Artifact同名的zip文件。这个ZIP文件解压到同名目录中,其实就是一个项目目录。

进入Intellij,选择File –> New –> Project From Existing Sources,然后选择下边的导入Maven项目,之后会识别出这个项目,如果已经打开Maven自动检测的话,现在Intellij就会开始配置这个项目了。

项目配置结束之后,可以看到自动写了一个类:

package cc.conyli.springbootdemo.mydemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MydemoApplication {

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

}

虽然还没有开始学,但是看这个代码,大概是知道SpringApplication是一个启动器,来运行我们这个类。其实这个时候能够看到Intellij右上角已经将这个类加入到了可运行中,而不像普通的Web项目需要配置服务器。先运行一下看看。

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.1.3.RELEASE)

2019-03-26 16:31:56.300  INFO 101568 --- [           main] c.c.s.mydemo.MydemoApplication           : Starting MydemoApplication on minko with PID 101568 (D:\Coding\Java\mydemo\target\classes started by Minko in D:\Coding\Java\mydemo)
2019-03-26 16:31:56.302  INFO 101568 --- [           main] c.c.s.mydemo.MydemoApplication           : No active profile set, falling back to default profiles: default
2019-03-26 16:31:57.451  INFO 101568 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2019-03-26 16:31:57.481  INFO 101568 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2019-03-26 16:31:57.481  INFO 101568 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.16]
2019-03-26 16:31:57.487  INFO 101568 --- [           main] o.a.catalina.core.AprLifecycleListener   : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [C:\Program Files\Java\jdk1.8.0_191\bin;C:\WINDOWS\Sun\Java\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\Program Files (x86)\Common Files\Oracle\Java\javapath;C:\Program Files\VanDyke Software\Clients\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;c:\users\minko\appdata\local\programs\python\python36\Scripts;C:\WINDOWS\System32\OpenSSH\;D:\Software\MySQL\bin\;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;D:\Software\Redis\;D:\Software\Git\cmd;D:\Software\Nodejs\;D:\Software\dev\node\lib\;C:\Program Files (x86)\Brackets\command;C:\Program Files\Java\jdk1.8.0_191\bin;;C:\Program Files\NVIDIA Corporation\NVIDIA NvDLISR;C:\Users\Minko\AppData\Local\Programs\Python\Python36\Scripts\;C:\Users\Minko\AppData\Local\Programs\Python\Python36\;C:\Users\Minko\AppData\Local\Microsoft\WindowsApps;C:\Users\Minko\AppData\Roaming\npm;D:\Software\Microsoft VS Code\bin;C:\Program Files\IntelliJ IDEA 2018.3.2\bin;;.]
2019-03-26 16:31:57.568  INFO 101568 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2019-03-26 16:31:57.568  INFO 101568 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1213 ms
2019-03-26 16:31:57.777  INFO 101568 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2019-03-26 16:31:57.966  INFO 101568 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2019-03-26 16:31:57.969  INFO 101568 --- [           main] c.c.s.mydemo.MydemoApplication           : Started MydemoApplication in 2.172 seconds (JVM running for 3.263)

Wow,这是什么。仔细查看一下,发现Tomcat initialized with port(s): 8080 (http),启动了Tomcat。访问localhost:8080,出现了如下页面:

Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.

Tue Mar 26 16:34:37 CST 2019
There was an unexpected error (type=Not Found, status=404).
No message available

没有配置/对应的路径,虽然是个错误页面,但是知道,这个Spring Boot程序已经成功运行,并且成为一个Web服务了。那后边就可以编写具体应用了。

Spring Boot REST 控制器

先来看看Spring Boot里怎么使用控制器,来尝试着写一个简单的REST控制器:

package cc.conyli.springbootdemo.mydemo.controller;


import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;

@RestController
public class MyRestController {

    @GetMapping("/")
    public String sayHello() {
        return "Hello! Time on server is " + LocalDateTime.now();
    }
}

停止掉运行的Spring Boot程序,再重新运行,再访问localhost:8080,结果发现输出已经有所改变。

刚才做的,实际上是Spring Boot为简化了配置,只需要简单的启动一个Java类,具体配置不用怎么关心,就可以编写业务代码了,而且可以发现,业务代码的编写与Spring MVC中是完全一致。

成功运行了第一个项目,现在可以来深入看一下Spring Boot了。

Spring Boot 项目结构与注解配置详解

项目结构

来看一下从https://start.spring.io/下载到的压缩包内的文件,这就是基础的项目文件:

  1. src/main/java/自定义包名,存放Java源代码的地方
  2. src/main/test/java/自定义包名,存放单元测试代码的地方
  3. src/main/resources,存放应用所需的配置文件,其实就是classpath对应的地方
  4. mvnw.cmd 和 mvnw,是Maven Wrapper File,让用户没有安装Maven也可以运行这个来下载和安装Maven进而配置项目,.cmd用于windows系统,mnvw用于linux系统。如果是用支持Maven的IDE,这两个文件可以忽略
  5. pom.xml,Maven配置文件。

项目入口类

再来看一下压缩包里自动生成的那个类:

package cc.conyli.springbootdemo.mydemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MydemoApplication {

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

这个类之前相当于是启动项目的一个入口。这里要解释的是@SpringBootApplication注解,这个注解相当于同时具有以下三个注解的功能:

  1. @EnableAutoConfiguration,启用Spring Boot的自动配置
  2. @ComponentScan,启用默认的扫描Bean的功能
  3. @Configuration,将当前类当做配置类,并且可以导入其他配置类

后两个其实就是Spring 原始的注解。这里要注意的是,由于这个入口类使用了@ComponentScan,所以默认扫描的范围就是这个类所在的同级类和包。一定要注意这一点。一般开发中,将这个入口程序放在你的程序包的根目录。

如果确实需要自己配置扫描路径,就采用如下写法:

@SpringBootApplication(scanBasePackages = {"cc.conyli","controller"})
public class MydemoApplication {

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

resources目录

这个目录之下有一个application.properties文件,这个文件是Spring Boot启动时自动创建的,默认为空,在其中可以添加Spring Boot的设置以及自定义的键值对。来添加两个:

server.port=8000
daughter.name=cony

然后在代码中,就可以使用取值表达式来取得其中的内容,修改一下我们的Rest控制器:

@RestController
public class MyRestController {

    @Value("${daughter.name}")
    private String name;

    @GetMapping("/")
    public String sayHello() {
        return "Hello " + name + "! Time on server is " + LocalDateTime.now();
    }
}

再启动服务,此时端口变成了8000端口,响应也读出了自定义的属性。

这里有一点要注意的是,如果要打包成Jar文件,不要使用src/main/webapp目录,否则会被大部分构建工具忽略,只有War文件需要部署到服务器目录下边之后,才需要这个目录。

对于Web开发,这个目录下有static目录,用于存放静态文件。

还有templates目录,用于存放模板文件,也就是JSP或者其他模板文件。Spring Boot对于一些第三方模板引擎都支持,比如FreeMarker,Thymeleaf,Mustache等。默认情况下Spring Boot就会从这个目录内载入模板文件。

最后就是单元测试的目录,测试也是很重要的一环,之后还是要好好学习一下测试的。

Spring Boot Starter

看完了项目结构,来实际看一下pom.xml文件:

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

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

这里没有像之前我们要一个一个将所有的依赖写好,直接使用了两个spring-boot的依赖,这个配置就来自于在https://start.spring.io/中选择的内容。

这其中还自动配置了Maven-plugin,可见真的非常方便。

那么starter-web和starter-test究竟是什么呢,其实就是封装好的一个所有兼容的版本库,省去一个一个配置具体需要的依赖。在https://start.spring.io/生成压缩包的时候,选择依赖的地方就有全部的starter依赖可供选择。

常用的Web开发的依赖有:

  1. spring-boot-starter-web,包含
  2. spring-boot-starter-security,包含Spring Security
  3. spring-boot-starter-data-jpa,包含JPA的支持,比如Hibernate

官网文档可以查看所有的starter依赖。

如果还是想要知道实际的依赖有哪些,Intellij中可以选择View –> Tool Windows –> Maven,然后在右侧的Dependencies中可以详细查看所有实际的依赖内容。经过查看可以看到里边包含有Spring MVC和全套依赖,Hibernate的Validator,集成Tomcat,Jackson等,涵盖了之前使用的大部分包。

对于这些配置在依赖里的starter,无需提供版本号。

Spring Boot还提供了一个特殊的Starter Parent,这是提供为Maven项目提供一些初始配置,可以在pom.xml文件中看到这样一段:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.3.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

与普通的starter不同的是,这个parent需要配置版本号。

只要使用网站上的项目生成器,就会自动包含这个parent。parent主要是配置了默认的编译级别(选的是Java 8),然后启用UTF-8支持,还有其他的一些配置。如果要修改parent的配置,需要在与parent标签同级的properties标签中设置具体的键(标签名)和值。

Spring Boot devtools

现在我们每修改一次代码,就要关闭然后重新运行项目入口。通过在pom.xml中加入一个依赖,就可以实现自动更新代码:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
</dependency>

devtools的文档在此。还有很多功能如果有空可以慢慢研究。

顺便一提的是这个功能现在很多IDE也可以实现。

Spring Boot Actuator

说到Spring Boot,不能不提的就是Actuator。这个是一个特殊的Spring Boot模块,不像其他模块用于业务开发,这个模块专门通过端点(就是URL路径)来向外界暴露当前应用的信息,很显然,这是一个非常好的开发辅助工具。这个工具就是Actuator。

要启用很容易,只需要在pom.xml中加入如下依赖:

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

在启用之后,项目自动会增加一个路径/actuator,可以通过这个路径获得各种信息,都是REST风格的JSON,常用的有:

  1. /actuator,简单的查询指南
  2. /actuator/health,应用运行状态
  3. /actuator/info,项目信息
  4. /actuator/beans,查看容器里所有的Bean
  5. /actuator/mappings,查看所有的URL映射关系

/actuator/health返回的JSON是:

{
    "status": "UP"
}

/actuator/info默认返回空,因为没有配置信息,可以在application.properties文件中配置一些:

info.app.name=SB Project
info.app.description=Sekiro is good and easy to play!
info.app.version=0.0.1

之后再访问,可以获得如下的JSON:

{
    "app": {
        "name": "SB Project",
        "description": "Sekiro is good and easy to play!",
        "version": "0.0.1"
    }
}

这里还能看到更多的暴露的端点,但是这里要注意的是,通过观察Spring Boot的启动日志以及文档,可以发现HTTP方式下默认仅暴露了2个端点health和info。

可以在application.properties文件中写一句来打开全部端点:

management.endpoints.web.exposure.include=*

端点信息安全防护

如果不想将信息暴露给外界,可以添加Spring Security:

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

Spring Security会自动配置Spring Boot Actuator相关的验证,而不像我们之前要创建初始化类和配置文件。观察控制台的输出:

Using generated security password: e0d04d71-b283-430e-948a-4e20a48f2422

可以得到密码,用户名默认是user,再访问除了/health和/info之外的端点,就会被要求输入用户名和密码。

如果想自定义用户名和密码,需要在application.properties文件中配置:

spring.security.user.name=cony
spring.security.user.password=li

这样就可以用自定义的用户名和密码来访问Spring Boot Actuator

如果要更详细的配置,比如通过Spring Security从数据库中读取用户和角色控制,可以具体配置Spring Security,针对/actuator下边的具体路径配置即可。

默认/health和/info无需认证,还可以在application.properties文件中配置关闭这两个端点:

management.endpoints.web.exposure.exclude=health,info

这样就在启用Spring Boot Actuator的同时用Spring Security进行了保护,防止信息泄露。

通过命令行运行Spring Boot

现代开发一般都通过使用IDE进行开发,运行程序也是通过IDE来运行。很多时候如果需要简单启动项目,可能需要在没有IDE的情况下运行。

在命令行模式下运行Spring Boot有两种方式:

  1. 在命令行下使用java -jar
  2. 在命令行下使用mvnw spring-boot:run

java -jar方式

以下是Windows的操作。关闭IDE,到项目的根目录下,运行:

mvnw package

会提示发现maven-wrapper.jar,如果没有安装Maven,就会自动下载,然后会慢慢将项目及所有依赖进行打包。这里要按几下回车才开始打包工作。看到如下内容说明打包成功:

[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO]
[INFO] --- maven-jar-plugin:3.1.1:jar (default-jar) @ mydemo ---
[INFO] Building jar: D:\Coding\Java\mydemo\target\mydemo-0.0.1-SNAPSHOT.jar
[INFO]
[INFO] --- spring-boot-maven-plugin:2.1.3.RELEASE:repackage (repackage) @ mydemo ---
[INFO] Replacing main artifact with repackaged archive
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  02:44 min
[INFO] Finished at: 2019-03-27T15:23:24+08:00
[INFO] ------------------------------------------------------------------------

如果已经安装了Maven,直接使用这条命令:

mvn package

就可以进行打包。

打包完成后可以看到提示,生成了mydemo\target\mydemo-0.0.1-SNAPSHOT.jar文件。

然后进入target目录,调用java虚拟机执行这个jar文件:

java -jar mydemo-0.0.1-SNAPSHOT.jar

之后可以在控制台看到和在IDE里一样的Spring Boot启动信息,测试一下访问,发现成功启动了。

mvnw spring-boot:run方式

将另外一个方法生成的jar包删除。在项目根目录输入:

mvnw spring-boot:run

可以看到编译了类之后直接启动了项目。