什么是SSMP?

SSMP和SSM差不多,只是将SSM中的Mybatis换成MybatisPlus,其中SSM是Spring、SpringMvc、Mybatis框架的整合,是标准的MVC模式,标准的SSM框架有四层,分别是dao层(mapper),service层,controller层和View层。使用spring实现业务对象管理,使用spring MVC负责请求的转发和视图管理,mybatis作为数据对象的持久化引擎。

我们下面讲解整合一下Junit、Mybatis、MybatisPlus、Druid,然后做一个小案例。

创建SpringBoot项目

创建SpringBoot有几种方式,一种是使用Maven来创建SpringBoot项目,一种是从官网配置项目信息,然后下载压缩包在本地打开,还有一种是直接在idea创建SpringBoot项目,下面演示在idea创建一个SpringBoot项目:

01-创建springboot项目

分析pom依赖

我们在创建SpringBoot项目时,如果不勾选任何依赖,在创建项目后,我们在pom.xml文件可以看到下面这段标签,这就是SpringBoot自带的Junit5 测试依赖。

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
使用Junit4版本
  1. 在pom.xml文件添加Junit4版本的依赖

    1
    2
    3
    4
    5
    6
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
    </dependency>
  2. 在测试类中,指定Junit4的运行器,测试类默认使用SpringRunner运行器,但是这个运行器是用于JUnit5的。因此,在测试类上可以添加@RunWith注解,并指定JUnit4的运行器

    1
    2
    3
    4
    @RunWith(JUnit4.class)
    public class MyTests {
    //...
    }

我们创建项目勾选了三个依赖,Spring Web、Mybatis、Mysql依赖,这三个基础依赖基本我们开发web项目是必备的。

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
    <dependencies>
<!-- Junit5测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Spring web项目-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<!-- mysql数据库-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>

上面依赖中,第一spring-boot-starter-test依赖是自带的Junit5测试,不需要我们指定添加,在开发中,我们每写完一个功能或方法时,我们基本都会使用到Junit来进行单元测试,基本是必备的,SpringBoot已经考虑到这点了,直接帮我们集成了。

第二个spring-boot-starter-web是SpringWeb项目需要的依赖,如以前我们没使用Spring框架开发时,每次创建web项目,都需要引入jakarta.servlet-api等等其他依赖,创建servlet环境,更好的支持.jsp

第三个mybatis-spring-boot-starter是Mybatis的依赖,从名字上可以看到有个starter,表示这是一个启动依赖,既然是叫启动依赖,应该是包括了很多基础东西吧,我们进入这个依赖看看,看看有什么东西。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</dependency>

这是mybatis-spring-boot-starter的启动依赖,可以看到,我们以前使用SpringWeb项目时,每次使用mybatis都要导入mybatis-springmybatisspring-jdbc依赖,这里SpringBoot直接使用mybatis-spring-boot-starter一个启动依赖,就包括了我们所需要的依赖,完全是简化了开发,我们不需要每次都一个个手打添加这么一堆依赖了。

第四个mysql-connector-j依赖就是我们常用连接mysql依赖了。

Junit运行测试

创建完项目后,我们可以在test目录下有一个测试类,这个测试类是默认生成的,这个测试类有一个初始化方法叫contextLoads

02-运行测试-20230430-012

我们在这个测试方法添加点东西,看看能不能运行,点击这个测试方法左边的绿色三角形运行测试

1
2
3
4
@Test
void contextLoads() {
System.out.println("springboot ....");
}

运行出错

03-运行出错-20230430-859

由和这个错误可以看到,系统提示我们没有指定数据库的url,大概就是要求我们添加链接数据库。好像也是,我们创建项目后,我们还没添加连接数据库的信息。

对于这个错误,有两个方法,一是添加数据库的链接信息,二是不连接数据库,忽略这个错误。

我们猜测,可能是我们添加的依赖自动集成到SpringBoot项目里面了,一运行SpringBoot项目就自动连接数据库,那我们是不是可以设置排除不连接数据库信息呢?这样做是可以的,我们在SpringBoot启动类设置忽略数据库。

解决方法

在SpringBoot启动类的注解添加exclude = {DataSourceAutoConfiguration.class}级可以忽略数据库的连接了,这样启动的时候,SpringBoot就不会去执行连接数据库的步骤就不会出错了。

1
2
3
4
5
6
@SpringBootApplication(exclude ={DataSourceAutoConfiguration.class} )
public class Springboot02SsmpApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot02SsmpApplication.class, args);
}
}

我们再去启动contextLoads测试就没问题了,我们再试试配置Mybatis。

04-测试成功-20230430-309

指定程序入口

启动类Springboot02SsmpApplication下面我以程序入口来代称。

可能你会好奇,为什么我们在程序入口配置忽略数据库连接,而不是在测试类?

我们在测试类运行方法,不应该是在测试类来配置忽略方法吗?更何况,我们在测试类似乎没有调用程序入口啊。

实际上,我们每一次在测试类运行方法,都会执行一次程序入口的,那测试类是怎么找到程序入口的呢?

一般测试类是通过自身的包路径来寻找程序入口的,因为我们在创建SpringBoot项目时,就填写了包的信息,而测试类和程序入口在生成时所在的包路径是相同的,只是根文件夹名不一样,一个是java,一个是test,而测试类就是根据这个包路径来寻找程序入口。

如果我们需要修改程序入口的程序位置,这是测试类又该如何找到程序入口呢?

毕竟系统还是系统嘛,还是做不到非常智能,没办法做到如何放都能找到,既然系统自发的行为找不到程序入口,那我们可以给它制定程序入口的位置。

@SpringBootTest注解上添加classes属性,指定程序入口的位置

1
2
3
4
5
6
7
8
9
10
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest(classes = Springboot02SsmpApplication.class)
class Springboot02SsmpApplicationTests {
@Test
void contextLoads() {
}
}

Junit+Mybatis+Druid

我们仍然是继续使用上面Junit的项目。

整合Junit+Mybatis步骤:

  1. 导入mybatis的启动依赖
  2. 配置数据库连接信息
  3. 编写SQL语句
  4. Junit单元测试

第一步我们在创建SpringBoot的项目已经导入了,其实就三步,非常简单。

配置数据库的连接信息

我们在application.properties配置文件配置我们的数据库信息,当我们打出username的关键词,SpringBoot就会提示我们可能需要输入的配置信息(YYDS),我们只需要回车就好了,然后配置我们数据库所需的信息。

05-配置数据库信息-20230430-655

因为我们的配置文件是.properties格式,我们填写的格式是key=value

1
2
3
4
spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.druid.url=jdbc:mysql://localhost:3306/spring_db?useUnicode=true&characterEncoding=utf-8
spring.datasource.druid.username=root
spring.datasource.druid.password=root

创建实体类

1
2
3
4
5
6
7
8
9
public class Book {
private Integer id;
private String type;
private String name;
private String description;

// get set toString 方法已省略

}

编写SQL语句

在开发中,我们可能使用接口的方式,也可能使用到XMl的方式,下面给出两种方式。

1
2
3
4
5
6
7
8
9
10
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import site.hikki.pojo.Book;
import java.util.List;

@Mapper
public interface BookDao {
@Select("select * from tb_book")
List<Book> getBook();
}

1. 编写接口

虽然我们使用xml,但我们仍然需要在接口上使用@mapper注解,用于标注该接口是mapper接口。

1
2
3
4
5
6
7
import org.apache.ibatis.annotations.Mapper;
import site.hikki.pojo.Book;

@Mapper
public interface BookDaoXML {
Book getBookById(Integer id);
}

2. 编写SQL语句

在resource下新建一个mapper文件夹,用于存放我们编写的sql语句。

06-mapper.xml文件-20230430-017

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="site.hikki.dao.BookDaoXML">
<select id="getBookById" parameterType="Integer" resultType="Book">
select * from tb_book where id=#{id}
</select>
</mapper>

3. 设置xml资源过滤

如果不作资源过滤,SpringBoot会自动拦截xml资源。

我们在pom.xml设置一下资源过滤

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
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<!-- 如果不添加此节点mybatis的mapper.xml文件都会被漏掉。 -->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.yml</include>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>

4. 配置Mybatis配置

是不是很意外,我们如果使用注解,mybatis甚至都不需要配置,直接就能使用了,是不是觉得很神奇,但我们使用xml方式的话,就需要配置一下了,我们需要指定mapper的xml文件的存放位置。以及我们使用xml方式,必须指定实体类别名,不然xml无法识别到实体类别名,你就需要使用包路径来指定实体类了,这样不方便开发。

application.properties配置下,再添加如下配置:

1
2
3
4
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=site.hikki.pojo
# 开启驼峰命名
mybatis.configuration.map-underscore-to-camel-case=true

Junit单元测试

我们上面使用了注解和xml方式编写SQL语句,这两者可以共存,但在开发中,我觉得使用xml更为方便一些,对于数据的结果集映射在xml更好用,比注解更方便编写。

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
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import site.hikki.dao.BookDao;
import site.hikki.dao.BookDaoXML;
import site.hikki.pojo.Book;
import javax.annotation.Resource;
import java.util.List;

@SpringBootTest(classes = Springboot02SsmpApplication.class)
class Springboot02SsmpApplicationTests {

@Autowired
private BookDao bookDao; // 注解方式

@Resource
private BookDaoXML bookDaoXML; // xml方式

@Test
void contextLoads() {
}

@Test
public void getBook(){
List<Book> book = bookDao.getBook();
for (Book b :book) {
System.out.println(b);
}
}

@Test
public void getBookById(){
Book book = bookDaoXML.getBookById(1);
System.out.println(book);
}
}

07-单元测试成功-20230430-890

总结

通过上面的学习,其实,创建一个Junit+Mybatis+Druid的项目,对于以前使用的Spring项目,已经减少了非常多的步骤了,减少了非常多的默认配置,很多配置的步骤已经省掉了,不需要我们动手配置,剩下一些必须自己手动配置的信息,比如数据库的连接信息,mapper.xml文件的存放路径。

Junit+MybatisPlus+Druid

Junit+MybatisPlus+Druid和上一个mybatis有点类似的,只是有关mybatis的配置换成mybatisplus的配置,并且mybatis的sql文件全都不用写,mybatisplus已经帮我们集成了我们常用的sql方法,比如数据基础的增删改查都帮我们写好了,我们只需要调用即可。

整合Junit+MybatisPlus步骤:

  1. 导入MybatisPlus的启动依赖
  2. 配置数据库信息
  3. 编写Dao接口
  4. 继承BaseMapper接口
  5. Junit单元测试

导入MybatisPlus依赖

我们一开始创建SpringBoot的项目时,没有选择到MybatisPlus的依赖,原因是IDEA没有收录MybatisPlus的依赖,所以就没有选择,必须由我们手动去添加。

1
2
3
4
5
6
<!--        mybatis plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>

配置数据库信息

数据库连接信息,和上面mybatis一样的

1
2
3
4
spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.druid.url=jdbc:mysql://localhost:3306/spring_db?useUnicode=true&characterEncoding=utf-8
spring.datasource.druid.username=root
spring.datasource.druid.password=root

提示:如果你的数据库表有前缀,你还需要在配置文件配置表前缀。

比如每个表都有前缀tb_,所以我们需要在配置中配置表前缀。

1
2
# 配置数据表的前缀 tb_
mybatis-plus.global-config.db-config.table-prefix=tb_

创建实体类

1
2
3
4
5
6
7
8
9
public class Book {
private Integer id;
private String type;
private String name;
private String description;

// get set toString 方法已省略

}

编写Dao接口

我们创建BookDao实体类时,仍然是需要使用@Mapper注解来表示该接口是mapper,我们只是标注该接口时mapper,还不行,我们可以看到@mapper注解的来源是ibatis,还不是mybatisplus的内容,我们需要继承mybatisplus的方法,才能使用mybatisplus的方法。

我们继承BaseMapper<T>,其中的T表示你的实体类对应的数据表名,根据名字来进行映射。如果你的数据表和实体类名称不一样,则会映射失败,也即是使用MybatisPlus继承的方法不能够使用,因为实体类和数据表无法进行映射。

1
2
3
4
5
6
7
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import site.hikki.pojo.Book;

@Mapper
public interface BookDao extends BaseMapper<Book> {
}

Junit单元测试

当我们调用bookDao接口中的方法时,你会发现,多了很多的方法,你在BookDao明明没写selectList等等这类方法,但这里出现了。细想一下,我们刚刚在BookDao是不是继承了BaseMapper接口,这些方法就来自这个接口了。

08-mybatisplus-20230501-214

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
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import site.hikki.dao.BookDao;
import site.hikki.pojo.Book;
import java.util.List;

@SpringBootTest(classes = Application.class)
class ApplicationTests {

@Autowired
private BookDao bookDao;

@Test
public void getBook(){
List<Book> books = bookDao.selectList(null);
for (Book b : books) {
System.out.println(b);
}
}

@Test
public void getBookById(){
Book book = bookDao.selectById(1);
System.out.println(book);
}
}

从ibatis–>Mybatis–>MybatisPlus,可以说功能逐一加强了,开发的步骤逐渐减少,MybatisPlus帮我们写好了一些基础的增删改查,我们只需要继续编写我们复杂的业务需求代码即可,节约了人力物力,加快开发速度。

SpringBoot配置高级

临时属性配置

我们在开发完了一个SpringBoot项目,当我们将这个项目打包成jar包或war包,里面的配置的固定了的。假设我们拿到这个SpringBoot项目,项目的服务端口是8080端口,但我的机器8080端口已经被其他程序使用了,这时候,我要怎么运行这个SpringBoot项目呢?难道我们要将打包好的SpringBoot程序修改吗?这样是不是非常麻烦?

SpringBoot提供了灵活的配置方式,如果你发现你的项目中有个别属性需要重新配置,可以使用临时属性的方式快速修改某些配置。方法也特别简单,在启动的时候添加上对应参数就可以了。

1
java –jar springboot.jar –-server.port=8081

我们加一个–-server.port指定端口运行就可以了,不需要重新修改打包好的程序。

如果你需要设置多个属性,你还可以添加多个临时属性,比如再添加一个日志输出

1
java –jar springboot.jar –-server.port=8081 --logging.level.root=error

属性之间是有空格分隔开,属性的格式类似properties文件的属性,使用.来分隔。

属性加载优先级

我们在启动jar包或war包是指定了临时属性,那属性的配置就有两个地方控制了,一个临时属性,一个是配置文件的属性。经过测试,我们使用临时的属性优先级比配置文件的属性优先级更高

看看官方文档的优先级排名:

地址:https://docs.spring.io/spring-boot/docs/current/reference/html/features.html

  1. 默认属性(通过 SpringApplication.setDefaultProperties 指定)
  2. @Configuration 类上的 @PropertySource 注解。请注意,这样的属性源直到application context被刷新时才会被添加到环境中。这对于配置某些属性来说已经太晚了,比如 logging.*spring.main.* ,它们在刷新开始前就已经被读取了。
  3. 配置数据(如 application.properties 文件)
  4. RandomValuePropertySource,它只有 random.* 属性
  5. 操作系统环境变量
  6. Java系统属性(System.getProperties()).
  7. java:comp/env 中的 JNDI 属性
  8. ServletContext初始化参数
  9. ServletConfig 初始化参数
  10. 来自 SPRING_APPLICATION_JSON 的属性(嵌入环境变量或系统属性中的内联JSON)
  11. 命令行参数
  12. 你在测试中的 properties 属性。在 @SpringBootTest 和测试注解中可用,用于测试你的应用程序的一个特定片断
  13. @TestPropertySource在你的测试中添加注释
  14. 当devtools处于活动状态时,$HOME/.config/spring-boot 目录下的Devtools全局设置属性

从上往下,优先级从低到高

从官方文档可以看到,我们使用命令行的临时属性在优先级中,排名11,这个顺序不用背下来的,知道就可以了,当我们在开发时,你就知道部署的时候应该要那个端口或者需要什么属性,你就应该加上去,而不是等开发完了再陆陆续续添加,这样会增加开发工作量。

总结

  1. 使用jar命令启动SpringBoot工程时可以使用临时属性替换配置文件中的属性
  2. 临时属性添加方式:java –jar 工程名.jar –-属性名=值
  3. 多个临时属性之间使用空格分隔
  4. 临时属性必须是当前boot工程支持的属性,否则设置无效

配置文件分类

上面我们疏导临时属性,可以在运行时设置临时属性,有优先级还记得SpringBoot的配置文件三种格式吗?

.properties.yml.yaml三种格式,优先级分别从高到低,我们一般将这几个配置文件放在resource资源根路径下,其实,配置文件的级别还存在其他方式,根据放的位置不同,优先级又不一样,那优先级分别有四个,如下:

  • 类路径下配置文件(resource资源根路径下的配置文件,正在用)
  • 类路径下config目录下配置文件
  • 程序包所在目录中配置文件
  • 程序包所在目录中config目录下配置文件

对上面四个优先级举个例子:

  1. file :config/application.yml 【最高】
  2. file :application.yml
  3. classpath:config/application.yml
  4. classpath:application.yml 【最低】

有多个优先级,当我们在开发、测试、上线时就可以根据不同的优先级选择不一样的优先级来配置属性,可以减少重复修改配置的时间和不必要的麻烦。

总结

  1. 配置文件分为4种

    • 项目类路径配置文件:服务于开发人员本机开发与测试
    • 项目类路径config目录中配置文件:服务于项目经理整体调控
    • 工程路径配置文件:服务于运维人员配置涉密线上环境
    • 工程路径config目录中配置文件:服务于运维经理整体调控
  2. 多层级配置文件间的属性采用叠加并覆盖的形式作用于程序

多环境开发

什么是多环境?其实就是指我们开发环境、测试环境、上线环境。我们在我们的电脑开发时,一般都是写独立的模块,然后在自己电脑环境使用Junit进行单元测试-->开发环境,然后当大家开发的差不多,就将全部开发者的模块整合再做一个整合环境测试,这时候使用的环境就是公司的服务器-->测试环境,再到项目开始上线了,这时候使用的是甲方公司的服务器-->生产环境

image-20230502000725536

我们在进行开发时,肯定是能简单就简单开发,所使用的环境肯定是和测试有所区别的,不可能完全一样,数据库连接的地址也不一样,使用本机的可能就是127.0.0.1:3600,但在公司的环境可能就不是这个了,到上线环境可能又不一样了,还有一些其他的配置属性,如果要一个个修改的话,非常麻烦,这时候就需要多套环境了,根据不同的环境来设置不同的值。

多环境多yaml文件版

这是主流的开发模式,还有一个模式是使用一个配置文件存放多个环境,这其实不太好,会导致配置文件的配置过多,造成混乱,所以还是使用一个环境使用一个文件最好妥当。

主配置文件

application.yml

1
2
3
spring:
profiles:
active: test # 指定环境名

开发环境

application-dev.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
spring:
datasource:
druid:
username: root
driver-class-name: com.mysql.cj.jdbc.Driver
password: root
url: jdbc:mysql://localhost:3306/spring_db?useUnicode=true&characterEncoding=utf-8

server:
port: 81

# mybatis配置
mybatis:
type-aliases-package: site.hikki.pojo
mapper-locations: classpath:mapper/*.xml # 指定mapper文件存放位置
configuration:
map-underscore-to-camel-case: true # 开启驼峰命名

测试环境

application-test.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
spring:
datasource:
druid:
username: root
driver-class-name: com.mysql.cj.jdbc.Driver
password: root
url: jdbc:mysql://localhost:3306/spring_db?useUnicode=true&characterEncoding=utf-8

server:
port: 82

# mybatis配置
mybatis:
type-aliases-package: site.hikki.pojo
mapper-locations: classpath:mapper/*.xml # 指定mapper文件存放位置
configuration:
map-underscore-to-camel-case: true # 开启驼峰命名

上线环境

application-pro.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
spring:
datasource:
druid:
username: root
driver-class-name: com.mysql.cj.jdbc.Driver
password: root
url: jdbc:mysql://localhost:3306/spring_db?useUnicode=true&characterEncoding=utf-8

server:
port: 83

# mybatis配置
mybatis:
type-aliases-package: site.hikki.pojo
mapper-locations: classpath:mapper/*.xml # 指定mapper文件存放位置
configuration:
map-underscore-to-camel-case: true # 开启驼峰命名

我们在主配置文件只设置选择环境,不作任何配置,可能你会好奇我们只写一个pro、dev、test就能知道系统选择哪一个环境的呢?其实你细看一下我们命名的配置文件,每个配置文件的命名都是有固定格式的。

每一个环境的配置名称格式:application-环境名.yml

这样在主配置文件设置环境名就可以搜索引入到配置环境了

SpringBoot最早期提供的配置文件格式是properties格式的,但现在使用的yml格式多一点,properties就不作讲解了,步骤和yml是一样的,只是配置的书写格式不一样而已,文件书写格式也是application-环境名.properties

多环境开发独立配置文件

我们在开发需要配置的内容非常多,当我们想配置某个环境,但又不想另一个环境,我们应该怎么做呢?要一个个文件修改嘛?还是挺麻烦的,我们就可以试着把某一类的配置独立出来到一个文件里面,然后在主配置文件引入就好了。

准备工作

创建多个配置文件,根据不同的类型配置信息放在不通的配置文件里面。

  • application.yml(主配置文件)
  • application-dev.yml
  • application-devDB.yml
  • application-devRedis.yml
  • application-devMVC.yml

application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
spring:
profiles:
active: dev
include: devDB,devMVC,devRedis

datasource:
druid:
username: root
driver-class-name: com.mysql.cj.jdbc.Driver
password: root
url: jdbc:mysql://localhost:3306/spring_db?useUnicode=true&characterEncoding=utf-8

# mybatis配置
mybatis:
type-aliases-package: site.hikki.pojo
mapper-locations: classpath:mapper/*.xml # 指定mapper文件存放位置
configuration:
map-underscore-to-camel-case: true # 开启驼峰命名

application-dev.yml

1
2
server:
port: 80

application-devDB.yml

1
2
server:
port: 81

application-devRedis.yml

1
2
server:
port: 82

application-devMVC.yml

1
2
3
4
server:
port: 83
servlet:
context-path: /hikki

使用

在主配置文件application.yml使用include属性在激活指定环境的情况下,同时对多个环境进行加载使其生效,多个环境间使用逗号分隔。

1
2
3
4
spring:
profiles:
active: dev
include: devDB,devMVC,devRedis

当我们指定使用dev配置文件时,就会先使用application-dev.yml这个配置文件,然后使用include导入其他的配置文件,当然,导入的文件名是没有固定的格式。

比如可以将devDB改为123DB也是可以的,但123DB的配置文件名应该是application-123DB.yml

加载顺序

我们查看控制台,可以看到或者的配置文件devDB,devMVC,devRedis,dev,并且端口是80,还配置了我们在devMVC配置的属性。

我们可以得出结论:

执行是有顺序的,并且dev是最后一个执行,因为可以看到端口是80,我们在application-dev.yml配置的端口是80,执行顺序按照devDB,devMVC,devRedis来执行,越是后面执行的,则会覆盖前面的配置

我们还在application-devMVC.yml配置了context-path,控制台也输出了,说明也执行了,得出结论:后面执行的配置只会覆盖前面存在的配置,前面不存在的配置是不会被覆盖的

12-多环境配置-20230502-011

改良

但是上面的设置也有一个问题,比如我要切换dev环境为pro时,include也要修改。因为include属性只能使用一次,这就比较麻烦了。SpringBoot从2.4版开始使用group属性替代include属性,降低了配置书写量。简单说就是我先写好,你爱用哪个用哪个。

application.yml

1
2
3
4
5
6
7
spring:
profiles:
active: dev
group:
"dev": devDB,devRedis,devMVC
"pro": proDB,proRedis,proMVC
"test": testDB,testRedis,testMVC

当我们需要那一组配置,只需要在active修改一下对应的组名就好了,非常方便。

多环境开发控制

多环境开发到这里基本上说完了,最后说一个冲突问题。就是maven和SpringBoot同时设置多环境的话怎么搞。

要想处理这个冲突问题,你要先理清一个关系,究竟谁在多环境开发中其主导地位。也就是说如果现在都设置了多环境,谁的应该是保留下来的,另一个应该遵从相同的设置。

maven是做什么的?项目构建管理的,最终生成代码包的,SpringBoot是干什么的?简化开发的。简化,又不是其主导作用。最终还是要靠maven来管理整个工程,所以SpringBoot应该听maven的。整个确认后下面就好做了。大体思想如下:

  • 先在maven环境中设置用什么具体的环境
  • 在SpringBoot中读取maven设置的环境即可

maven中设置多环境(使用属性方式区分环境)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<profiles>
<profile>
<id>env_dev</id>
<properties>
<profile.active>dev</profile.active>
</properties>
<activation>
<activeByDefault>true</activeByDefault> <!--默认启动环境-->
</activation>
</profile>
<profile>
<id>env_pro</id>
<properties>
<profile.active>pro</profile.active>
</properties>
</profile>
</profiles>

SpringBoot中读取maven设置值

1
2
3
spring:
profiles:
active: @profile.active@

上面的@属性名@就是读取maven中配置的属性值的语法格式。

总结

  1. 当Maven与SpringBoot同时对多环境进行控制时,以Mavn为主,SpringBoot使用@…@占位符读取Maven对应的配置属性值
  2. 基于SpringBoot读取Maven配置属性的前提下,如果在Idea下测试工程时pom.xml每次更新需要手动compile方可生效

日志

日志其实就是记录程序日常运行的信息,主要作用如下:

  • 编程期调试代码
  • 运营期记录信息
  • 记录日常运营重要信息(峰值流量、平均响应时长……)
  • 记录应用报错信息(错误堆栈)
  • 记录运维过程数据(扩容、宕机、报警……)

记录日志

我们可以对每一条请求进行日志记录,用于分析用户的信息。

添加日志记录操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping
public class HelloController {
private static final Logger log = LoggerFactory.getLogger(HelloController.class);

@GetMapping("hello")
public String hello(){
System.out.println("--------");
log.debug("hello debug...");
log.info("hello info...");
log.warn("hello warn...");
log.error("hello error...");
return "hello 小码同学!!!";
}
}

上述代码中log对象就是用来记录日志的对象,下面的log.debug,log.info这些操作就是写日志的API了。

设置日志输出级别

日志设置好以后可以根据设置选择哪些参与记录。这里是根据日志的级别来设置的。日志的级别分为6种,分别是:

  • tarce:运行堆栈信息,使用率低
  • debug:程序员调试代码使用
  • info:记录运维过程数据
  • warn:记录运维过程报警数据
  • error:记录错误堆栈信息
  • fatal:灾难信息,合并计入ERROR

​ 一般情况下,开发时候使用debug,上线后使用info,运维信息记录使用warn即可。下面就设置一下日志级别:

1
2
# 开启debug模式,输出调试信息,常用于检查系统运行状况
debug: true

​ 这么设置太简单粗暴了,日志系统通常都提供了细粒度的控制

1
2
3
4
5
6
7
# 开启debug模式,输出调试信息,常用于检查系统运行状况
debug: true

# 设置日志级别,root表示根节点,即整体应用日志级别
logging:
level:
root: debug

​ 还可以再设置更细粒度的控制

设置日志组

控制指定包对应的日志输出级别,也可以直接控制指定包对应的日志输出级别

1
2
3
4
5
6
7
8
9
10
11
logging:
# 设置日志组
group:
# 自定义组名,设置当前组中所包含的包
ebank: site.hikki.controller
level:
root: warn
# 为对应组设置日志级别
ebank: debug
# 为对包设置日志级别
site.hikki.controller: debug

说白了就是总体设置一下,每个包设置一下,如果感觉设置的麻烦,就先把包分个组,对组设置,没了,就这些。

总结

  1. 日志用于记录开发调试与运维过程消息
  2. 日志的级别共6种,通常使用4种即可,分别是DEBUG,INFO,WARN,ERROR
  3. 可以通过日志组或代码包的形式进行日志显示级别的控制

优化日志对象创建代码

我们在记录日志时,每次都要创建一个日志记录对象,这个还能优化一下,我们之前有使用过lombok工具包,我们之前使用了@data,省略get、set方法的书写,它还有另外一个功能,它可以为我们提供log日志对象,不需要我们创建,我们直接使用@Slf4j注解即可。

1
2
3
4
5
6
7
8
9
10
11
@RestController
@RequestMapping
public class HelloController {
private static final Logger log = LoggerFactory.getLogger(HelloController.class); //用了@Slf4j注解可不写

@GetMapping("hello")
public String hello(){
log.debug("hello debug...");
return "hello 小码同学!!!";
}
}

导入依赖

1
2
3
4
5
<!--        lombok提供log日志对象-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>

使用lombok创建log对象

我们可以直接使用log对象,不需要创建,lombok给我们提供了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
@RequestMapping
public class HelloController {

@GetMapping("hello")
public String hello(){
System.out.println("--------");
log.debug("hello debug...");
log.info("hello info...");
log.warn("hello warn...");
log.error("hello error...");
return "hello 小码同学!!!";
}
}

查看控制台,我们输出的日志信息。

13-日志输出-20230502-374

日志输出格式控制

日志已经能够记录了,但是目前记录的格式是SpringBoot给我们提供的,如果想自定义控制就需要自己设置了。先分析一下当前日志的记录格式。

14-日志分析-20230502-111

对于单条日志信息来说,日期,触发位置,记录信息是最核心的信息。级别用于做筛选过滤,PID与线程名用于做精准分析。

1
2
3
logging:
pattern:
console: "%d %clr(%p) --- [%16t] %clr(%-40.40c){cyan} : %m %n"

日志文件

日志信息显示,记录已经控制住了,下面就要说一下日志的转存了。日志不能仅显示在控制台上,要把日志记录到文件中,方便后期维护查阅。

对于日志文件的使用存在各种各样的策略,例如每日记录,分类记录,报警后记录等。这里主要研究日志文件如何记录。

记录日志到文件中格式非常简单,设置日志文件名即可。

1
2
3
logging:
file:
name: server.log

虽然使用上述格式可以将日志记录下来了,但是面对线上的复杂情况,一个文件记录肯定是不能够满足运维要求的,通常会每天记录日志文件,同时为了便于维护,还要限制每个日志文件的大小。下面给出日志文件的常用配置方式:

1
2
3
4
5
logging:
logback:
rollingpolicy:
max-file-size: 3KB
file-name-pattern: server.%d{yyyy-MM-dd}.%i.log

完整配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 滚动日志,达到文件大小,自动写入新文件
logging:
# 设置分组
group:
ebank: site.hikki.controller,site.hikki.dao
iservice: com.alibaba
level:
root: info
# 设置某个包的日志级别
# 设置分组,对某个组设置日志级别
ebank: warn

# 设置日志模板格式
pattern:
# console: "%d - %m %n"
console: "%d %clr(%5p) --- [%16t] %clr(%-40.40c){cyan} : %m %n"
file:
name: server.log
logback:
rollingpolicy:
max-file-size: 4KB
file-name-pattern: server.%d{yyyy-MM-dd}.%i.log

以上格式是基于logback日志技术设置每日日志文件的设置格式,要求容量到达3KB以后就转存信息到第二个文件中。文件命名规则中的%d标识日期,%i是一个递增变量,用于区分日志文件。

总结

  1. 日志记录到文件
  2. 日志文件格式设置

15-日志文件输出-20230502-115

热部署

开启热部署

导入依赖

1
2
3
4
5
<!--        热部署工具 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>

开启相关配置

启动自动构建项目:文件-->构建、执行、部署-->编译器-->打勾自动构建项目

允许自动maek启动:文件-->高级设置-->编译器-->打勾及时开发的应用程序当前正在运行,也允许自动make启动

13-开启热部署-2

测试

如果你每敲一个字母,服务器就重新构建一次,这未免有点太频繁了,影响开发,所以idea设置当idea工具失去焦点5秒后进行热部署。其实就是你从idea工具中切换到其他工具时进行热部署,比如改完程序需要到浏览器上去调试,这个时候idea就自动进行热部署操作。

参与热部署范围

如果你有修改过项目的其他配置文件,你就会知道,并不是所有的文件修改都会激活热部署的,有些文件修改时不激活热部署的,比如SpringBoot的配置文件,resource下面的一些其他文件。

原因在于在devtools开发者工具中有一组配置,当满足了配置中的条件后,才会激活热部署,配置中默认不参与热部署的目录如下:

  • /META-INF/maven
  • /META-INF/resources
  • /resources
  • /static
  • /public
  • /templates

以上目录中的文件如果发生变化,是不参与热部署的。如果想修改配置,可以通过application.yml文件进行设定哪些文件不参与热部署操作

1
2
3
4
5
spring:
devtools:
restart:
# 设置不参与热部署的文件或文件夹
exclude: static/**,public/**,config/application.yml

关闭热部署

热部署一般用于我们在开发时最多,调试效率高,当项目完成时,需要上线项目了,我们就需要关闭热部署了,防止项目上线后不稳定,我们可以通过在SpringBoot的配置文件关闭热部署功能:

1
2
3
4
spring:
devtools:
restart:
enabled: false

如果你在SpringBoot的配置文件添加了上述信息,没有生效,可能是配置文件层数过多,配置相互覆盖,导致没有生效,你可以使用更高的优先级设置,比如在main函数入口添加设置属性,当然,你想设置其他属性,也是可以在main函数入口设置属性的。

1
2
3
4
5
6
7
@SpringBootApplication()
public class Application {
public static void main(String[] args) {
System.setProperty("spring.devtools.restart.enabled","false");
SpringApplication.run(Application.class, args);
}
}

本文章来源于我的博客:https://blog.hikki.site