主要内容

  • Spring Boot技术栈
    • 介绍Spring Boot完整的技术栈(图片格式,暂时没有上传)
      • Web应用
      • 数据操作
      • 消息
      • 测试
      • 运维管理
  • Spring Boot构建方式
    • 图形化
    • 命令行
  • Spring Boot多模块应用
    • 构建分层、多模块应用
  • Spring Boot运行方式
    • IDEA
    • 命令行
    • Maven插件
  • Spring Boot简单应用
    • Spring Web MVC
    • Spring Web Flux技术
    • 编程简单应用
  • Spring Boot三大特性
    • 自动装配
    • 嵌入式容器
    • 为生产准备的特性

定义

  • 应用
    • 功能性
      • 系统所设计的业务范畴
    • 非功能性
      • 安全
      • 性能
      • 监控
      • 数据指标(CPU利用率、网卡使用率)
      • production-ready(为产品而准备)
  • 特性
    • 约定大于配置
      • 大多数组件不需要自行配置,而是自动装配
      • 简化开发
      • 例如maven
    • 独立Spring容器,不需要外部依赖
      • 例如依赖Tomcat
      • 嵌入式Tomcat、Jetty

  • 外部配置
    • 启动参数
    • 配置文件
    • 环境变量
  • 外部应用
    • Servlet应用
    • Spring Web MVC
    • Spring Web Flux
    • WebSocket
    • WebService

Spring Boot构建方式

  • 图形化方式
  • 命令行方式(Maven)
    • mvn archetype:generate -DgroupId=com.bai -DartifactId=first-spring-boot-app -Dversion=1.0.0-SNAPSHOT -DinteractiveMode=false
  • 修改pom文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<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 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--不加parent会报错:spring-boot-starter-webflux没有指定版本-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.M7</version>
</parent>

<groupId>com.bai</groupId>
<artifactId>first-spring-boot-app</artifactId>
<packaging>jar</packaging>
<version>1.0.0-SNAPSHOT</version>
<name>first-spring-boot-app</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
</dependencies>
<!--当前使用的2.0.0M7版本在maven库中没有,只存在在spring的中央库内-->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/libs-milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
  • 修改App.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.bai;

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

/**
* Hello world!
*
* @SpringBootApplication
* 和
* @SpringBootConfiguration
* @EnableAutoConfiguration
* @ComponentScan
* 等价
*/
@SpringBootApplication
public class App
{
public static void main( String[] args )
{
SpringApplication springBootApplication=new SpringApplication(App.class);
springBootApplication.run(args);
}
}

Q : 为什么new SpringApplication的时候需要把App.class传进去

A : @SpringBootApplication打在了App这个类上,App就相当于配置类

注意点

  • 启动时容器为内置Netty
1
2
3
INFO 10206 --- [ctor-http-nio-1] r.ipc.netty.tcp.BlockingNettyContext     : Started HttpServer on /0:0:0:0:0:0:0:0:8080
INFO 10206 --- [ main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 8080
INFO 10206 --- [ main] com.bai.App : Started App in 3.529 seconds (JVM running for 4.775)

一些理论

Mono 和 Flux 是 Reactive streams 的发布者实现。

  • body

    • Mono

      • 0-1元素

      • Optional(Java8)

      • 1
        2
        Collection<User> users=userRepository.findAll();
        Mono<Collection<User>> userMono=Mono.just(users);
    • Flux

      • 0-N元素

      • 类似于Iterable或者Collection

      • 1
        2
        Collection<User> users=userRepository.findAll();
        Flux<User> userFlux=Flux.fromIterable(users);
      • Req -> WebFlux -> 1-N线程执行函数式任务

      • 推的方式

Mono和Flux都是Publisher。

Publisher -> publish(1)

Subscription(1):订阅消息

Subs(A)#onNext() -> Subs(B)#onNext() -> Subs(C)#onNext()

Java9里面API称之为Flow,可以理解为责任链,也叫观察者模式。

Reactive是推模式

Iterable是拉模式

补充

编码

  • 如果方法返回的是集合,尽量返回
    • 原子的
    • 只读的

书籍

  • 《Effective Java》- 第二版

架构

  • Spring-webFlux不能和Spring-webmvc同时使用

资料

1
2
3
4
//官方示例(错误)
RouterFunction<ServerResponse> helloWorldRoute =
RouterFunctions.route(RequestPredicates.path("/hello-world"),
request -> Response.ok().body(fromObject("Hello World")));
1
2
3
4
//正确版本
RouterFunction<ServerResponse> helloWorldRoute =
RouterFunctions.route(RequestPredicates.path("/hello-world"),
request -> ServerResponse.ok().body(fromObject("Hello World")));

构建多模块应用

  • 修改主工程类型 jar -> pom
1
<packaging>pom</packaging>
  • 新建web工程,将遗留代码全部移动到web/java目录下
  • 再从web工程,独立出model工程
  • 将web工程依赖model工程
  • 重复步骤3,独立出persistence
  • 再从persistence添加model的依赖
  • 最终依赖关系 web -> persistence -> model

补充

1
2
3
4
5
6
<dependency>
<groupId>com.bai</groupId>
<artifactId>persistence</artifactId>
<!--和当前项目版本保持一致-->
<version>${project.version}</version>
</dependency>

运行方式

  • IDE
  • 命令行
  • Maven脚本

构建可执行jar

  • 项目根路径下执行mvn -Dmaven.text.skip -U clean package
  • cd web/target
  • java -jar web-1.0.0-SNAPSHOT.jar

no main manifest attribute, in web-1.0.0-SNAPSHOT.jar?

jar包里有MANIFEST.MF,有Main-Class的属性,API:java.util.jar.Manifest#getAttributes

需要一个Spring Boot的插件。

1
2
3
4
5
6
7
8
9
<!--web下的pom文件-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

会保找不到插件,需要添加插件仓库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!--总项目的pom文件-->    
<pluginRepositories>
<pluginRepository>
<id>spring-milestones</id>
<url>http://repo.spring.io/milestone</url>
</pluginRepository>
</pluginRepositories>

<!--当前使用的2.0.0M7版本在maven库中没有,只存在在spring的中央库内-->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/libs-milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>

构建方式从jar切换为war

  • 修改web下pom文件
1
<packaging>war</packaging>
方式一
  • 创建webapp/WEB-INF(相对于src/main目录)
  • 新建空的web.xml

上两步是为了绕过war插件的限制

方式二
1
2
3
4
5
6
7
<plugin>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<!--取消web.xml验证-->
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>

启动多个

正常启动一个后,默认端口被占用,这时候需要用其它端口来启动服务。

java -jar web-1.0.0-SNAPSHOT.war --server.port=0

--server.port=0随机挑选一个可用端口

server.por源码位置:

org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration -> ServerProperties类中的prefixserverport就是下面的属性。

maven启动

  • 项目跟路径下执行mvn spring-boot:run
    • 如果报本地仓库没有则执行:mvn -Dmaven.test.skip -U clean install,然后再启动就可以了。

补充

  • 解压web-1.0.0-SNAPSHOT
    • BOOT-INF
      • 这是Spring Boot1.4开始才有的。
  • 当使用依赖或者插件时,如果版本是Milestone,需要增加
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<url>http://repo.spring.io/snapshot</url>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<url>http://repo.spring.io/milestone</url>
</pluginRepository>
</pluginRepositories>

<!--当前使用的2.0.0M7版本在maven库中没有,只存在在spring的中央库内-->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/libs-milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
  • META-INF/MANIFEST.MF里面有指定
    • Main-Class
    • Start-Class
1
2
Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: com.bai.App
  • 除了jar或者war启动的方式,还有目录启动方式
    • 可以帮助解决过期的jar不支持Spring Boot新方式,比如老版本的MyBatis
      • 如果是jar包
        • cd /web/target/web-1.0.0-SNAPSHOT
        • java org.springframework.boot.loader.JarLauncher
      • 如果是war包
        • cd /web/target/web-1.0.0-SNAPSHOT
        • java org.springframework.boot.loader.WarLauncher

其它

自动装配模式:xxxAutoConfiguration

不同版本中名称可能会发生变化。

问题及回答

Q:webFluxConfiguration里面的映射路径和controller里面的路径有什么区别?

A:基本上没有区别,注意不要重复定义或URL语义有重复。

Q:webFlux不是跟MVC不能一起吗,怎么启动了

A:spring-boot-starter-webmvc和spring-boot-starter-webflux可以放在同一个应用,可是webFlux不会工作,默认使用webmvc,webFlux不会被采用。其实webFlux是兼容Annotation驱动,比如@RequestMapping

Q:webFlux可以定义rest吗

A:可以。

Q:Spring的老项目迁移到spring-boot,如何操作

A:老的XML方式采用@ImportResource导入

Q:嵌入式Tomcat如何调优

A:两种方式:

  • 通过application.properties文件调整参数
  • 通过接口回调:TomcatConnectorCustomizerTomcatContextCustomizer