本文提供了一个例子,展示了Spring Boot怎么帮你加速程序的开发。随着你阅读更多的教程,你会看到更多的Spring Boot的使用示例。本文带你快速体验一下Spring Boot。如果你想创建你自己的基于Spring Boot的项目,访问Spring Initializr(如果无法访问的话,可以用阿里云的Aliyun Java Initializr代替),填充你的项目信息,设置你的选项,然后下载打包成zip的项目文件。
本文目标
用Spring Boot构建一个简单的Web应用,然后添加一下有用的服务。
你需要
- 15分钟左右
- IntelliJ IDEA
- JDK 1.8+
- Maven 3.2+
学习可以用Spring Boot做什么
利用Spring Boot可以快速构建程序。它会查找你配置的classpath和bean,对于你缺失的配置,用合理的设想进行填充。使用Spring Boot你可以更专注于业务,减少对基础架构的投入。
下面的例子可以很好的说明Spring Boot可以为你做什么:
- 添加了Spring MVC依赖?这有有几个特殊的bean经常需要添加,然后Spring Boot会自动添加他们。一个Spring MVC应用还需要一个servlet容器,然后Spring Boot会自动配置一个内置的Tomcat。
- 添加了Jetty依赖?那么你可能不需要Tomcat,相对的需要一个内置的Jetty。Spring Boot会自动为你配置。
- 添加了Thymeleaf依赖?那么有一些bean需要添加到application上下文。Spring Boot会自动为你添加。
Spring Boot自动配置还有更多的例子,这里不再一一列举。同时,Spring Boot并没有屏蔽你的手动配置。比如说,你添加了Thymeleaf依赖,Spring Boot会自动添加一个SpringTemplateEngine到application上下文。但是,你一旦添加了你自己设定的SpringTemplateEngine,Spring Boot就不会再自动添加SpringTemplateEngine。这样的控制方式让你可以更轻松的完成自己的项目。
Spring Boot并不会生成代码或者修改你的编码。相对的,当你启动程序的时候,Spring Boot会动态的引入bean和设置到你的程序上下文。
用Spring Initializr生成项目代码
对于所有的Spring应用,你都可以使用Spring Initializr生成基本的项目代码。Initializr提供了一个快速的方式去引入所有你需要的依赖,并且为你做了很多设置。当前例子只需要一个Spring Web依赖。具体设置如下图:
如上图所示,我们选择了Maven作为编译工具。你也可以选择Gradle来进行编译。然后我们分别把Group和Artifact设置为“com.hansy”和“spring-boot”。
生成的pom.xml文件内容如下:
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.hansy</groupId>
<artifactId>spring-boot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<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>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
创建简单的Web应用
我们为这个简单的Web应用,创建一个Web控制器,代码如下:
package com.hansy.springboot;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@RequestMapping("/")
public String index(){
return "Greetings from Spring Boot";
}
}
控制器类添加了@RestController注解,表示是Spring MVC的网络请求处理类。@RequestMapping注解把请求地址“/”映射到index()方法。当从浏览器访问或者在命令行里面使用curl访问,index()方法会返回一段纯文字。这是因为@RestController整合了@Controller和@ResponseBody两个注解,这两个注解使网络请求直接返回数据而不是一个视图View。
创建一个Application类
Spring Initializr默认创建了一个简单的application类。然后,在本例中,它太简单了。修改代码之后如下:
package com.hansy.springboot;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import java.util.Arrays;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
return args -> {
System.out.println("Let's inspect the beans provided by Spring Boot:");
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName :
beanNames) {
System.out.println(beanName);
}
};
}
}
@SpringBootApplication是一个便利的注解,相当于添加了如下注解:
- @Configuration 定义配置类,可替换xml注解,被注解的类内部可包含一个或多个@Bean注解的方法。
- @EnableAutoConfiguration 将检查类路径classpath中的依赖关系,并且在某些情况下它会配置某些东西,比如当它检测到数据库驱动程序时,就会配置数据库。还执行其他配置,包括配置/调用帮助程序组件(如Web应用程序中的嵌入式tomcat)。
- @ComponentScan 使Spring在“com/hansy”下面,查找组件,配置,服务和控制器等通过注解标识的类。
main()方法使用Spring Boot的SpringApplication.run()方法来启动一个Application。你可以看到这里没有一行XML,也没有web.xml。这个Web应用是100%的纯Java的,并且不用去研究繁琐的框架配置。
我们添加了一个标识为@Bean的CommandLineRunner方法,他会在程序启动时运行。这个方法会获取所有的被应用创建的Bean和Spring Boot自动添加的Bean。然后把这些Bean排序,并打印出来。
运行程序
输出结果如下:
Let's inspect the beans provided by Spring Boot:
application
applicationAvailability
applicationTaskExecutor
basicErrorController
beanNameHandlerMapping
beanNameViewResolver
characterEncodingFilter
commandLineRunner
conventionErrorViewResolver
defaultServletHandlerMapping
defaultViewResolver
dispatcherServlet
dispatcherServletRegistration
error
errorAttributes
errorPageCustomizer
errorPageRegistrarBeanPostProcessor
formContentFilter
handlerExceptionResolver
handlerFunctionAdapter
helloController
httpRequestHandlerAdapter
jacksonObjectMapper
jacksonObjectMapperBuilder
jsonComponentModule
lifecycleProcessor
localeCharsetMappingsCustomizer
mappingJackson2HttpMessageConverter
mbeanExporter
mbeanServer
messageConverters
multipartConfigElement
multipartResolver
mvcContentNegotiationManager
mvcConversionService
mvcHandlerMappingIntrospector
mvcPathMatcher
mvcResourceUrlProvider
mvcUriComponentsContributor
mvcUrlPathHelper
mvcValidator
mvcViewResolver
objectNamingStrategy
.......
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration$TomcatWebServerFactoryCustomizerConfiguration
.......
tomcatServletWebServerFactory
tomcatServletWebServerFactoryCustomizer
tomcatWebServerFactoryCustomizer
.......
你可以看到org.springframework.boot.autoconfigure开头的Bean,还有tomcatServletWebServerFactory。
访问服务器通过curl命令,输出结果如下:
$ curl localhost:8080
Greetings from Spring Boot
添加单元测试
你添加的控制器需要测试的话,可以使用Spring的Test模块的相关功能。
pom.xml中加入对应的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
现在写一个简单的单元测试,模拟服务器请求和响应。
代码如下:
package com.hansy.springboot;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import static org.hamcrest.Matchers.equalTo;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
@AutoConfigureMockMvc
public class HelloControllerTest {
@Autowired
private MockMvc mvc;
@Test
public void getHello() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().string(equalTo("Greetings from Spring Boot")));
}
}
MockMvc类是Spring Test的一部分,可以让你使用一组方便的Builder类,发送HTTP请求到DispatcherServlet,然后对结果使用断言进行判断。可以看到,我们使用@AutoConfigureMockMvc注解和@SpringBootTest注解来注入一个MockMvc实例。一旦使用了@SpringBootTest注解,就会创建整个应用的上下文环境。另一种做法,则是通过使用@WebMvcTest注解,使Spring Boot只创建Web层的上下文。不论是那种情况,Spring Boot都会尝试定位你的应用的入口程序类,如果你想实现不同的功能的话,你可以重载或者缩小它。
就像模拟HTTP请求的流程,你也可以用Spring Boot去实现一个简单的全栈集成测试。
代码如下:
package com.hansy.springboot;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.http.ResponseEntity;
import java.net.MalformedURLException;
import java.net.URL;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class HelloControllerIT {
@LocalServerPort
private int port;
private URL base;
@Autowired
private TestRestTemplate template;
@BeforeEach
public void setUp() throws MalformedURLException {
this.base = new URL("http://localhost:" + port + "/");
}
@Test
public void getHello() {
ResponseEntity<String> response = template.getForEntity(base.toString(), String.class);
assertThat(response.getBody()).isEqualTo("Greetings from Spring Boot");
}
}
因为我们设置了 webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT ,内置服务器每次启动都会运行在一个随机的端口。通过设置 @LocalServerPort 注解,我们可以在运行时获取到实际的端口。
添加应用监控服务
如果你想创建一个商用网站,你可能需要添加一些管理服务。Spring Boot通过actuator模块提供了几个相关的服务(例如:健康、审计、Beans等等)。
使用actuator模块需要在pom.xml添加对应的依赖,代码如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
然后重新应用。
你可以看到一组新的RESTful Endpoint添加到了应用里面。这是Spring Boot提供的管理服务。下面列出几条关键的输出:
management.endpoint.configprops-org.springframework.boot.actuate.autoconfigure.context.properties.ConfigurationPropertiesReportEndpointProperties
management.endpoint.env-org.springframework.boot.actuate.autoconfigure.env.EnvironmentEndpointProperties
management.endpoint.logfile-org.springframework.boot.actuate.autoconfigure.logging.LogFileWebEndpointProperties
management.endpoints.jmx-org.springframework.boot.actuate.autoconfigure.endpoint.jmx.JmxEndpointProperties
management.endpoints.web-org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties
management.endpoints.web.cors-org.springframework.boot.actuate.autoconfigure.endpoint.web.CorsEndpointProperties
management.health.diskspace-org.springframework.boot.actuate.autoconfigure.system.DiskSpaceHealthIndicatorProperties
management.health.status-org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorProperties
management.info-org.springframework.boot.actuate.autoconfigure.info.InfoContributorProperties
management.metrics-org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties
management.metrics.export.simple-org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleProperties
management.server-org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties
actuator模块提供的endpoint如下:
其中,还有一个/actuator/shutdown的endpoint,但是默认只在JMX中可见。如果想要把它作为HTTP的endpoint,你需要在application.properties中,添加如下配置:
management.endpoint.shutdown.enabled=true
management.endpoints.web.exposure.include=health,info,shutdown
然而,你可能不应该在一个提供公共服务的应用中开启shutdown的endpoint。
你可以通过如下命令检测一个应用的健康状态:
$ curl localhost:8080/actuator/health
{“status”:“UP”}
你也可以在不开启shutdown的情况下,尝试用curl调用shutdown。
结果如下:
$ curl -X POST localhost:8080/actuator/shutdown
{“timestamp”:“2020-07-07T13:06:10.849+00:00”,“status”:404,“error”:“Not Found”,“message”:"",“path”:"/actuator/shutdown"}
因为我们没有激活它,shutdown的endpoint也就不存在。
而开启之后,我们调用shutdown的话,结果如下:
$ curl -X POST localhost:8080/actuator/shutdown
{“message”:“Shutting down, bye…”}
查看Spring Boot的Starter
你可以查看一些常用的Starter,或者直接查看所有Starter的源码。
Jar支持和Groovy支持
上面的例子展示了Spring Boot自动加载一些必须的类。还展示了怎么开启便利的管理服务。
但是,Spring Boot还能做更多。依靠Spring Boot强大的加载器模块,它不仅支持WAR文件的部署,还支持生成可执行的Jar文件。它通过spring-boot-gradle-plugin和spring-boot-maven-plugin两个插件,分别支持Gradle和Maven两种编译方式。
除此以外,Spring Boot也支持Groovy,可以使你的Spring MVC的web应用只有一个源文件。
创建一个文件名为app.groovy的文件,文件的内容如下:
@RestController
class ThisWillActuallyRun {
@RequestMapping("/")
String home() {
return "Hello World!"
}
}
这个文件放在哪里都行。
然后,安装Spring Boot CLI。
下载spring-boot-cli-2.3.1.RELEASE-bin,解压到某个目录,接着把里面的bin文件夹添加到Path系统变量。
打开一个新的命令行窗口,通过如下命令运行Groovy应用:
$ spring run app.groovy
运行Groovy应用之前,需要先关闭以前的应用,防止端口冲突。
再打开一个新的命令行窗口,运行curl命令访问Groovy应用,结果如下:
$ curl localhost:8080
Hello World!
Spring Boot动态添加关键注解到你的代码里面,然后使用Groovy Grape拉取需要的运行库来执行Groovy应用。
小结
我们用Spring Boot创建了一个简单的Web应用,并且学到了Spring Boot是怎么提升你的开发速度的。接着开启了一些有用的应用服务。这仅仅只是一篇简单的Spring Boot使用例子,接下来我们会学习更多的Spring Boot的使用方法。