写作时间:2018-12-20
Spring Boot: 2.1 ,JDK: 1.8, IDE: IntelliJ IDEA,
Spring Boot 2.1的好处
Spring Boot
提供了快速方式去建立应用. 应用的classpath
和beans
已默认配置好, Spring Boot
已经搭建好架构基础设施,你可以专注于业务开发.
举栗子?:
- 想要
Spring MVC
? 你需要设置相关beans
,Spring Boot
已经自动设置好.Spring MVC app
需要servlet container
,Spring Boot
自动配置并内置了Tomcat
. - 想要
Jetty
容器? 要是这样, 你不需要Tomcat
, 你需要内置的是Jetty
.Spring Boot
也帮你内置了Jetty
容器. - 想要
Thymeleaf
? 相关beans
需要设置在application context
;Spring Boot
已经为你设置好.
Spring Boot
还可以定制化,比如, Thymeleaf
已经被用到, Spring Boot
自动加入SpringTemplateEngine
在你的application context
. 但是如果你自定义了SpringTemplateEngine
(设置不用默认方式), 那么 Spring Boot
就不做处理,把控制权留给你.
下面将一步一步实现第一个Spring Boot工程,本系列教材都是基于2.1
版本。
创建一个简单web application
创建Web application
流程:
打开Idea-> create new Project ->Spring Initializr ->填写group、artifact ->钩上web(开启web功能)->点下一步就行了。(下面有具体步骤图片)
- create new Project
- Spring Initializr
- 填写group、artifact
- 钩上web(开启web功能)
- 就这么简单,
Spring Boot
工程建立好了。
在简单的web application
创建一个web controller
.
Controller
规范是放在package web
中
路径: src/main/java/web/HelloController.java
package web;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
@RestController
public class HelloController {
@RequestMapping("/")
public String index() {
return "Greetings from Spring Boot!";
}
}
HelloController
上面的注解@RestController
, 表示用Spring MVC
去处理web requests
. @RequestMapping
设置默认访问路径/
, 将由方法index()
处理. 用浏览器访问http://localhost:8080/
或者在command line
中用命令curl
+ http://localhost:8080/
, 将返回一个纯文本. 这是因为注解@RestController
有 @Controller
和 @ResponseBody
组合而成, 这两个注解表示请求结果返回data,而不是view. 点击看源码就一清二楚:
package org.springframework.web.bind.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Controller;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
@AliasFor(
annotation = Controller.class
)
String value() default "";
}
创建Application class
删掉默认创建了Application class
, com.zgpeace.demo1boot.Demo1bootApplication.java
新建web.Application.java
代码如下:
package web;
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 Demo1bootApplication {
public static void main(String[] args) {
SpringApplication.run(Demo1bootApplication.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
是个方便的注解,包含了一下内容:
@SpringBootConfiguration
包含了@Configuration
,表示该类作为定义bean
的application context
.@EnableAutoConfiguration
表示Spring Boot
把classpath
下的beans
,Spring Boot
配置的beans
, 和各种各样的property
设置,都加载进来.- 通常
Spring MVC app
需要加注解@EnableWebMvc
,Spring Boot
自动加这个注解当它看到classpath
包含spring-webmvc
. 这个标识该工程为web application
, 并激活key behaviors
(比如设置DispatcherServlet
). @ComponentScan
通知Spring
在Controller当前的package里去寻找其它components
,configurations
,services
, 允许它找到controllers.
main()
方法启动了Spring Boot
的SpringApplication.run()
方法去启动一个application
. 你会发现不需要任何XML配置文件? 也没有web.xml . 这个web application
是个100%纯Java
.
CommandLineRunner
方法标识为@Bean
, 并在启动时运行. 它检索所有的beans
, 有的是工程创建的类,有的是Spring Boot
自动引入. 排序并打印输出.
把默认的main class删掉以后,发现运行有错误,点击Edit Configuration
选择为新建类web.Application
运行application
- 点击右上角的运行按钮:
如果用Maven,cd到工程的根目录,运行命令:
mvn package && java -jar target/demo1boot-0.0.1-SNAPSHOT.jar
- 可以看到控制台结果输出:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.1.RELEASE)
2018-12-20 10:21:15.445 INFO 5901 --- [ main] c.z.demo1boot.Demo1bootApplication : Starting Demo1bootApplication on zgpeaces-MacBook-Pro.local with PID 5901 (/Users/zgpeace/Intellij-workspace/demo1boot/target/classes started by zgpeace in /Users/zgpeace/Intellij-workspace/demo1boot)
2018-12-20 10:21:15.450 INFO 5901 --- [ main] c.z.demo1boot.Demo1bootApplication : No active profile set, falling back to default profiles: default
2018-12-20 10:21:16.201 INFO 5901 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2018-12-20 10:21:16.217 INFO 5901 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2018-12-20 10:21:16.218 INFO 5901 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/9.0.13
...
Let's inspect the beans provided by Spring Boot:
application
beanNameHandlerMapping
defaultServletHandlerMapping
dispatcherServlet
embeddedServletContainerCustomizerBeanPostProcessor
handlerExceptionResolver
helloController
httpRequestHandlerAdapter
messageSource
mvcContentNegotiationManager
mvcConversionService
mvcValidator
org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration
org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration
org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration
org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration$DispatcherServletConfiguration
org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration$EmbeddedTomcat
org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration
org.springframework.boot.context.embedded.properties.ServerProperties
org.springframework.context.annotation.ConfigurationClassPostProcessor.enhancedConfigurationProcessor
org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration
propertySourcesBinder
propertySourcesPlaceholderConfigurer
requestMappingHandlerAdapter
requestMappingHandlerMapping
resourceHandlerMapping
simpleControllerHandlerAdapter
tomcatEmbeddedServletContainerFactory
viewControllerHandlerMapping
...
打印输出可以看到org.springframework.boot.autoconfigure
, tomcatEmbeddedServletContainerFactory
.
在控制台访问链接:
$ curl localhost:8080
Greetings from Spring Boot!
增加单元测试 Unit Tests
pom.xml默认引入了Unit Tests依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
新增类 src/test/java/web/HelloControllerTest.java
package web;
import org.junit.Test;
import org.junit.runner.RunWith;
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.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.hamcrest.Matchers.equalTo;
@RunWith(SpringRunner.class)
@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,允许你方便的编译类, 发送HTTP请求给DispatcherServlet
, 并断言结果assertions. 用@AutoConfigureMockMvc
和@SpringBootTest
去注入MockMvc
实例. 例子用@SpringBootTest
去创建了整个application context
. 轻量级的选择用注解@WebMvcTest
,制动web层的application context
.
运行单元测试,可以在HelloController
的’index()'方法里面打个断点,测试通过:
第二种方式模拟HTTP请求周期,可以用Spring Boot
去实现网络请求整个流程的集成测试:
新建类: src/test/java/web/HelloControllerIT.java
package web;
import static org.junit.Assert.*;
import static org.hamcrest.Matchers.equalTo;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
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 org.springframework.test.context.junit4.SpringRunner;
import java.net.URL;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class HelloControllerIT {
@LocalServerPort
private int port;
private URL base;
@Autowired
private TestRestTemplate template;
@Before
public void setUp() throws Exception {
this.base = new URL("http://localhost:" + port + "/");
}
@Test
public void getHello() throws Exception {
ResponseEntity<String> response = template.getForEntity(base.toString(), String.class);
assertThat(response.getBody(), equalTo("Greetings from Spring Boot!"));
}
}
内置的Tomcat启动用了一个随机的端口webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
, 实际的端口在运行时runtime显示@LocalServerPort
.
增加 production-grade services
项目都需要监控服务的状态,Spring Boot 提供了很多actuator module,比如health, audits, beans.
在pom.xml里面增加如下依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
如果依赖没有生效,可能是没有下载构建成功,可以手动mvn clean, mvn build一遍。
点击右上角的运行按钮:
actuator
里面的组件概览:
$ curl localhost:8080/actuator
{"_links":
{"self":
{"href":"http://localhost:8080/actuator","templated":false},
"health":{"href":"http://localhost:8080/actuator/health","templated":false},
"health-component":{"href":"http://localhost:8080/actuator/health/{component}",
"templated":true
},
"health-component-instance":{"href":"http://localhost:8080/actuator/health/{component}/{instance}","templated":true},
"info":{"href":"http://localhost:8080/actuator/info","templated":false}
}
}
检查应用的health
$ curl localhost:8080/actuator/health
{"status":"UP"}
检查shutdown
,因为没有启用,比较友好的提示:
% curl -X POST localhost:8080/actuator/shutdown
{"timestamp":"2018-12-20T05:46:41.856+0000","status":404,
"error":"Not Found","message":"No message available",
"path":"/actuator/shutdown"}%
查看 Spring Boot’s starters
已经看了一些 Spring Boot’s “starters”. 你可以点击此查看全部.
JAR支持 和 Groovy支持
Spring Boot支持单独的WAR包发布,也可以打包成可执行的jar包,上面Maven运行有例子。默认打包方式支持Maven和Gradle, 内置的插件spring-boot-maven-plugin
和spring-boot-gradle-plugin
.
Spring Boot还支持Groovy, 你可以用单个Groovy文件构建Spring MVC web apps.
创建文件ThisWillActuallyRun.groovy
, 实现代码如下:
@RestController
class ThisWillActuallyRun {
@RequestMapping("/")
String home() {
return "Hello World!"
}
}
接下来安装 Spring Boot’s CLI.
笔者把解压缩文件放到目录:
/Users/zgpeace/opt/spring-2.1.1.RELEASE
笔者Mac的命令行用的是myzsh
,先跳转配置文件位置.zshrc
, 编辑.zshrc
$ cd $home
$ vim .zshrc
笔者配置Java相关运行环境,记录一下
# JAVA
export JAVA_HOME="$(/usr/libexec/java_home -v 1.8)"
export MAVEN_HOME="/Users/zgpeace/opt/apache-maven-3.5.4"
export SPRING_HOME="/Users/zgpeace/opt/spring-2.1.1.RELEASE"
export PATH=$JAVA_HOME/bin:$MAVEN_HOME/bin:$SPRING_HOME/bin:$PATH
# spring shell auto-completion
ln -s $SPRING_HOME/shell-completion/zsh/_spring /usr/local/share/zsh/site-functions/_spring
重新加载配置文件
$ source .zshrc
查看Spring版本
$ spring --version
Spring CLI v2.1.1.RELEASE
cd到ThisWillActuallyRun.groovy
所在的目录, 运行命令启动应用
$ spring run ThisWillActuallyRun.groovy
命令行Terminal打开新的窗口访问:
$ curl localhost:8080
Hello World!
Spring Boot 动态地在代码中增加key annotations
,用Groovy Grape去拉取类库使APP运行起来.
代码布局规范
通常建议将应用的main类放到其它类所在包的顶层(root package),并将@EnableAutoConfiguration注解到你的main类上,这样就隐式地定义了一个基础的包搜索路径(search package),以搜索某些特定的注解实体(比如@Service,@Component等) 。例如,如果你正在编写一个JPA应用,Spring将搜索@EnableAutoConfiguration注解的类所在包下的@Entity实体。
采用root package方式,你就可以使用@ComponentScan注解而不需要指定basePackage属性,也可以使用@SpringBootApplication注解,只要将main类放到root package中。
下面是一个典型的结构:
com
+- example
+- myproject
+- Application.java
|
+- domain
| +- Customer.java
| +- CustomerRepository.java
|
+- service
| +- CustomerService.java
|
+- web
+- CustomerController.java
总结
恭喜你! 你成功创建了Spring Boot应用并加速了你的开发速度. 例子只列举了有限的服务,亲可以查看更多在线文档.
例子代码:https:///zgpeace/Spring-Boot2.1/tree/master/demo1boot