SpringBoot常用技术整合和使用

本文环境基于 springboot 2.0.1.RELEASE , springboot 要求JDK8+

认识 SpringBoot

Spring Boot : 与 Spring4 一起诞生,是一个微框架。

  • 整合了许多子项目,使用很少的配置就可以十分快速的搭建和运行项目。约定优先于配置,减少样板化的配置。
  • 自动装配:springMVC,jdbc,事务等。
  • 使用嵌入式容器:tomcat,netty,jetty等。不需要打成 war 包,就可以放入 tomcat 中运行。
  • 提供可视化的相关功能,方便监测,比如性能,应用的健康程度等。
  • 为微服务 SpringCloud 铺路,SpringBoot 可以整合很多框架来构建微服务,比如 dubbo,thrift 等。
相关比较 说明
spring framework spring 框架。
spring boot 快速构建 spring 应用。
spring cloud springboot 分布式云应用。

快速起步

SpringBoot 提供了一个快速初始化项目的网站 https://start.spring.io

当然我们也可以构建一个普通的 Maven 项目并引入相关依赖。

springboot 项目需要继承 spring-boot-starter-parent ,其会提供一些默认参数。

1
2
3
4
5
6
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath/>
</parent>

快速构建 web 项目,只要引入 spring-boot-starter-web 即可,其已包含了 tomcat 插件。正如所见,其会继承父 pom 的版本号。

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

springboot 使用 main方法 启动,其会自动扫描包内的相关配置等。

注意 : 如下的示例,只会扫描 tk.gushizone.demo 及其子包的内容。

1
2
3
4
5
6
7
8
9
10
11
12
package tk.gushizone.demo;

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

@SpringBootApplication
public class DemoApplication {

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

仅需以上的配置,即可启动运行一个简单的 Web 项目,就是这么简单!

例如添加如下 Rest 风格的 Controller,进行测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package tk.gushizone.demo.controller;

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

@RestController
public class UserController {

@PostMapping("/hello")
public User save(){
return "Hello SpringBoot!";
}

}

目录结构

一个简单的 springboot 的目录如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
demo
├── pom.xml
└── src
├── main
│   ├── java
│   │   └── tk
│   │   └── gushizone
│   │   └── demo
|   │ ├── ... # 可扫描的包
│   │   └── DemoApplication.java # 启动类
│   │  
│   └── resources
│   ├── application.yml # 配置文件
│   ├── static # 静态资源
│   └── templates # 模板
└── test

配置文件

springboot 默认支持通过 application.propertiesapplication.yml 配置属性。两者可以并存,且会优先参考 .properties ,若相关配置没有,会尝试从 .yml 中获取。

1
2
3
4
5
6
7
# api 端口号
server.port=8080
# url 路径
server.servlet.context-path=/demo
# session 超时时间
server.servlet.session.timeout=60m
server.tomcat.uri-encoding=utf-8

yaml 是当前流行的配置方式,较 .properties 展示树状结构更清晰,较 .xml 更加简洁。

下文中为使配置项更清晰,会以 .properties 的方式配置。

1
2
3
4
5
6
7
8
server:
port: 8080
servlet:
# context-path: /demo
session:
timeout: 60m
tomcat:
uri-encoding: utf-8

环境隔离

一般开发 ,测试,上线等环境是不同的,springboot 支持多环境的文件配置。可以创建 application-{profile}.propertiesapplication-{profile}.yml 等配置文件,使用 spring.profiles.active 指定当前生效的配置文件。

1
spring.profiles.active: dev

使用 yml 配置时,也可以配置在一个文件中配置。 不推荐 !

1
2
3
4
5
6
7
8
9
spring:
profiles:
active: dev
---
spring:
profiles: dev
---
spring:
profiles: prod

读取配置

可以在 .yml 中直接使用已定义的值。

1
2
3
4
5
default:
value: 123456
system:
username: admin
password: ${default.value}

在 Java 中获取值,并设置默认值。

1
2
3
4
5
6
7
8
9
import org.springframework.beans.factory.annotation.Value;

...

@Value("${default.system.username:admin}")
private String defaultUsername;

@Value("${default.system.password:123}")
private Integer defaultPassword;

读取自定义文件

一般我们会将配置文件放在 resource 目录,默认编译行为会将其输出到更目录,通过 ClassLoader 可以获取 classpath 根目录路径。

注意 : 文件的读取是依赖当前环境的目录结构,并不是项目目录结构。例如 编译和打包后,目录结构都是不同的。

1
2
3
String fileName = "mmall.properties";

ClassLoader classLoader = Test.class.getClassLoader();
1
2
3
4
5
// 方式一,推荐
InputStream in = classLoader.class.getResourceAsStream(fileName);

// 方式二,不推荐,在打包后可能无法正常获取。
File configFile = new File(classLoader.getResource(fileName).getFile(););
1
2
3
// 属性文件读取
Properties props = new Properties();
props.load(new InputStreamReader(classLoader.getResourceAsStream(fileName),"UTF-8"));

整合 Log

springboot 整合了一个 sl4j 依赖,可以直接使用 log4j 和 logback 。当然也可以单独引入。

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>

推荐使用 Logback。

整合Thymeleaf

完成如下配置即可使用 Thymeleaf,详情参考 :

pom.xml

Spring Boot 1.x 中包含了thymeleaf-layout-dialect ,但在 Spring Boot 2.x 中被移除。

1
2
3
4
5
6
7
8
9
<!-- thymeleaf 和 布局方言 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>nz.net.ultraq.thymeleaf</groupId>
<artifactId>thymeleaf-layout-dialect</artifactId>
</dependency>

application.properties

1
2
3
4
5
6
7
8
9
10
11
12
13
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML5
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.servlet.content-type=text/html
# 关闭缓存,即时刷新
spring.thymeleaf.cache=false

# 设置静态文件路径,js,css 等
# 请求路径
spring.mvc.static-path-pattern=/public/**
# 资源路径
#spring.resources.static-locations=classpath:/static/

messages*.properties

顺便提一下国际化配置,文件默认必须以 messages 开头命名,可以重写 MessageSourceAutoConfiguration

/src/main/resources/i18n/messages_zh_CN.properties

1
home.welcome=欢迎来到我们的杂货店!

整合 Mybatis

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
<!-- pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.3</version>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
<!-- DB -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.9</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.41</version>
</dependency>

一般 *Mapper.xml 会放在 resources 下,但是这样开发和管理时不是很方便。这时可以放在 java 目录内,添加以下配置,可以确保被复制到输出目录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>

<resource>
<directory>${project.basedir}/src/main/resources</directory>
<targetPath>${project.build.outputDirectory}</targetPath>
</resource>
</resources>
</build>

application.properties

没有扫描的xml并不影响启动,但使用时会出现 org.apache.ibatis.binding.BindingException: Invalid bound statement (not found) 异常 。

1
2
3
4
5
6
7
8
9
10
11
# mybatis
# pojo 的位置 (配置该项后,可以在xml中不使用全路径引用实体类)
mybatis.type-aliases-package=tk.gushizone.common.pojo
# xml 的位置
mybatis.mapper-locations=classpath*:tk/gushizone/**/dao/sqlmap/*.xml,classpath*:mapper/*.xml

# pagehelper
pagehelper.helperDialect=mysql
pagehelper.reasonable=true
pagehelper.supportMethodsArguments=true
pagehelper.params=count=countSql
1
2
3
4
5
6
7
8
9
10
11
12
# db
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

# druid
spring.datasource.druid.initial-size=1
spring.datasource.druid.min-idle=1
spring.datasource.druid.max-active=20
spring.datasource.druid.test-on-borrow=true
spring.datasource.druid.stat-view-servlet.allow=true
1
2
3
# 热部署 - mybatis相关 ( 需要配置devtools )
restart.include.mapper=/mapper-[\\w=\\.]+jar
restart.include.pagehelper=/pagehelper-[\\w=\\.]+jar

Application.java

@MapperScan 用于扫描 DAO层 接口,生成对应的 bean。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package tk.gushizone.mybatis;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan(basePackages = {"tk.gushizone.**.dao"})
public class MyBatisApplication {

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

热更新

热部署热加载 都是指不重启服务,完成更新编译/部署项目,其都是基于Java的类加载器实现。

涉及 部署方式 实现原理 使用场景
热部署 热部署在服务器运行时重新部署项目 热部署直接重新加载整个应用,存在内存释放。 热部署更多在生产环境使用。
热加载 热加载在运行时重新加载class 热加载在运行时重新加载class,监控文件是否改变,直接修改 JVM 内的字节码文件。 热加载更多在开发环境使用。

可以看出我们一般说的热部署其实都是热加载,这里也是介绍一下springboot热加载方式。

热加载方式 说明
- 默认情况下springboot可以完成对 .java 的热更新,至少在 IDE 中是这样 🤣 。
Devtools 使用快速重启的方式,可以完成对 .java.xml 的热更新。
Springloaded 可以完成对 .java 的热更新。
IDE 插件 如 JRebel ,收费插件,这里不会介绍。

-

Eclipse

eclipse 默认会自动编译,不需要任何设置,即可完成热更新。

IDEA

IDEA 默认不会自动编译,需要设置。

  • Preferences -> Build,Execution,Deployment -> Compiler -> Build project automaticaly
  • command + shift + ctrl + / -> Registry -> compiler.automake.allow.when.app.running
  • 编辑应用启动配置: Edit Configuration -> Running Application Update Policies -> Update classes and resources

如果还是不行,可以点击右上角的小锤子,或 command + F9

Devtools

Devtools 在项目较大时,启动依然会慢 🤣。

实现对类文件的热部署,文件发生改变时会立即重启应用,因为采用虚拟机机制,重启会很快。

pom.xml

1
2
3
4
5
6
7
<!-- devtools -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<!-- optional=true, 依赖不会传递 -->
<optional>true</optional>
</dependency>

application.properties

1
2
3
4
5
6
# 热部署 - thymeleaf
spring.thymeleaf.cache=false

# 热部署 - mybatis相关
restart.include.mapper=/mapper-[\\w=\\.]+jar
restart.include.pagehelper=/pagehelper-[\\w=\\.]+jar

Springloaded

spring-boot-maven-plugin 插件下添加 springloaded 依赖。

如果不能自动下载,先放到上面的 dependencies 中即可下载。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>tk.gushizone.system.SystemApplication</mainClass>
</configuration>
<!--mvn spring-boot:run-->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
<version>1.2.6.RELEASE</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>

maven方式

IDEA 中直接 Terminal 进行 clean installmvn spring-boot:run 即可。

1
2
3
4
5
6
7
8
# 项目根目录
cd ../app-demo
# 将项目依赖打包并放入本地仓库
mvn -Dmaven.test.skip -U clean install
# 主模块目录
cd web
# maven方式 启动 springboot项目
mvn spring-boot:run

JVM 启动参数

需要下载 jar 包,记住路径,并给 JVM 添加启动参数。

1
-javaagent:/users/username/developer/lib/springloaded-1.2.6.RELEASE.jar -noverify

Junit

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

Junit 的测试方法一定要是 public

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
package org.demo.test;

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.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;

import org.demo.Application;
import org.demo.user.UserService;

@RunWith(SpringRunner.class)
@ActiveProfiles("dev")
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class JunitTest {
@Autowired
private UserService userService;

@Test
public void test1() throws BatchException {
userService.queryByPage();
}

}

打包 & 运行

这里仅介绍 jarwar 的打包和运行。

打包种类

在应用启动模块 的 pom 规定打包方式 , 其默认为 jar

注意 :当设置为 war 时,需要满足含有 main/webapp/WEB-INF/web.xml ,即使为空文件。

1
<packaging>war</packaging>

一般的打包种类有 : jar , war , ear ,详情如下:

打包方式 说明
jar Java Archive file ,一般把开发时要引用通用(JAVA)类及资源做封装,打成包后便于存放管理。
war Web Archive file ,一个(web)完整的应用,通常是网站或WEB平台,打成包后可以部署到容器中。
ear Enterprise Archive file ,企业级应用,实际上EAR包中包含WAR包和几个企业级项目的配置文件而已,服务器中间件通常选择WebSphere等都会使用EAR包。通常是EJB打成ear包。

编译插件和启动类

springboot 提供了编译打包的maven插件,如果希望可以直接运行,还需要指定启动类: mainClass

方式一

1
2
3
4
5
6
7
8
9
10
11
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>org.demo.DemoApplication</mainClass>
</configuration>
</plugin>
</plugins>
</build>

方式二

1
2
3
<properties>
<start-class>org.demo.DemoApplication</start-class>
</properties>

打包并运行

进入项目根目录,执行以下命令:

1
2
# 跳过测试,更新第三方包,clean,package
mvn -Dmaven.test.skip -U clean package

打包成功后,可以在 主模块 /web/target 下看到压缩包。

1
2
3
4
5
6
7
# 打包为 jar 时:
# web-0.0.1-SNAPSHOT.jar # 包含该所有依赖 和 项目 class
# web-0.0.1-SNAPSHOT.jar.original # 项目 class

# 打包为 war 时:
# web-0.0.1-SNAPSHOT.war # 包含该所有依赖 和 项目 class
# web-0.0.1-SNAPSHOT.war.original # 项目 class 和依赖

jar方式,启动项目

1
2
3
4
5
# 打包为 jar 时:
java -jar web-0.0.1-SNAPSHOT.jar

# 打包为 war 时:
java -jar web-0.0.1-SNAPSHOT.war

maven 方式运行

1
2
3
4
5
6
7
8
# 项目根目录
cd ../app-demo
# 将项目依赖打包并放入本地仓库
mvn -Dmaven.test.skip -U clean install
# 主模块目录
cd web
# maven方式 启动 springboot项目
mvn spring-boot:run