写作时间:2018-12-20
Spring Boot: 2.1 ,JDK: 1.8, IDE: IntelliJ IDEA,

Spring Boot 2.1的好处

Spring Boot提供了快速方式去建立应用. 应用的classpathbeans 已默认配置好, 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功能)->点下一步就行了。(下面有具体步骤图片)

  1. create new Project
  2. springboot 2最新稳定版本 springboot2.1_Spring

  3. Spring Initializr
  4. springboot 2最新稳定版本 springboot2.1_Java_02

  5. 填写group、artifact
  6. springboot 2最新稳定版本 springboot2.1_Java_03

  7. 钩上web(开启web功能)
  8. springboot 2最新稳定版本 springboot2.1_Java_04

  9. 就这么简单,Spring Boot工程建立好了。
  10. springboot 2最新稳定版本 springboot2.1_SpringBoot_05

在简单的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 ,表示该类作为定义beanapplication context.
  • @EnableAutoConfiguration 表示Spring Bootclasspath下的beans, Spring Boot配置的beans, 和各种各样的property设置,都加载进来.
  • 通常Spring MVC app需要加注解@EnableWebMvc, Spring Boot 自动加这个注解当它看到classpath包含spring-webmvc. 这个标识该工程为web application, 并激活key behaviors(比如设置DispatcherServlet).
  • @ComponentScan通知SpringController当前的package里去寻找其它components, configurations, services, 允许它找到controllers.

main() 方法启动了Spring BootSpringApplication.run()方法去启动一个application. 你会发现不需要任何XML配置文件? 也没有web.xml . 这个web application是个100%纯Java.

CommandLineRunner 方法标识为@Bean, 并在启动时运行. 它检索所有的beans, 有的是工程创建的类,有的是Spring Boot自动引入. 排序并打印输出.

把默认的main class删掉以后,发现运行有错误,点击Edit Configuration

springboot 2最新稳定版本 springboot2.1_SpringBoot_06

选择为新建类web.Application

springboot 2最新稳定版本 springboot2.1_springboot 2最新稳定版本_07

运行application

  1. 点击右上角的运行按钮:

如果用Maven,cd到工程的根目录,运行命令:

mvn package && java -jar target/demo1boot-0.0.1-SNAPSHOT.jar
  1. 可以看到控制台结果输出:
.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: 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()'方法里面打个断点,测试通过:

springboot 2最新稳定版本 springboot2.1_Spring_08

第二种方式模拟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一遍。

springboot 2最新稳定版本 springboot2.1_SpringBoot_09

点击右上角的运行按钮:

springboot 2最新稳定版本 springboot2.1_Spring_10

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-pluginspring-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