前不久,springboot3.1新增了一个依赖spring-boot-docker-compose,本文就来讲解一下该依赖的功能。
我们在进行本地开发时经常使用到MySQL,Redis,RabbitMQ,MongoDB,Elasticsearch等诸多环境,可能很多人为了安装方便,会采用docker安装的形式。但是使用docker安装的环境也会存在一些问题:
如果将容器内部的端口映射到宿主机随机端口,每次重启可能导致端口号改变,因此需要修改java项目中的连接地址上的端口号。
如果将容器内部端口映射到宿主机的固定端口,虽然可以解决重启容器导致端口号改变的问题,但是如果需要启动多个相同容器时,多个容器需要挨个指定端口号,并且在java项目中的连接地址上配置对应的端口号,服务太多时难免导致混乱。
于是就有了今天的解决方案,springboot3.1新增的功能,只需要在项目下放置compose.yaml文件,并配置使用到的数据库容器镜像以及对应的容器类型,即可在我们启动springboot项目时自动创建容器,在结束springboot项目进程时自动销毁容器。并且,无需我们配置数据库连接地址,用户名和密码信息,项目启动后会自动连接上容器内部的数据库。我们也可以将容器内部的文件映射到宿主机,保证数据的持久化。
可能有些人还是觉得该功能并没有什么实际的作用,个人认为该功能对开发测试而言,功能真的很强大。比如开发时常用redis作为缓存,而使用该功能后,我们无需特意安装Redis容器,甚至可以说我们不需要任何操作,就可以直接用Redis的功能。单元测试时,我们也可以使用该功能,保证你每次启动都能使用一个全新的数据库,结合springboot自身的数据库脚本初始化功能,可以说是相当完美了。下面讲解如何使用:
一、创建springboot3项目
版本必须在3.1以上,目前最新版3.1.1。需要引入以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-docker-compose</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
还有一些其他依赖,比如lombok之类的常用依赖我就不列出了。
二、项目根目录下创建compose.yaml文件,内容如下:
services:
db1:
image: mysql:latest
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: test_db
ports:
- "3306"
labels:
org.springframework.boot.service-connection: mysql #标识容器类型,比如mysql,redis等等
文件内容和使用docker-compose部署MySQL时几乎一样,但是需要注意的是ports节点和labels节点部分。平时我们部署MySQL一般会写成这样:
ports:
- "3306":"3306"
作用是将容器内部3306端口号映射到宿主机3306端口,而我们这里只需要指定容器内部端口即可,容器会自动将内部端口映射到宿主机的随机端口上,然后springboot会自动找到映射到宿主机的随机端口进行连接。
这里解释一下这两行配置的作用:
labels:
org.springframework.boot.service-connection: mysql #标识容器类型,比如mysql,redis等等
如果是官方提供的数据库镜像,我们可以不写这两行。springboot自动识别数据库类型,然后找到对应的ConnectionDetails接口实现,为我们初始化数据源。如果是非官方的镜像,比如被我们魔改过的MySQL镜像,springboot并不能识别他是MySQL镜像。因此,我们可以在上面两行配置中标识他是一个MySQL容器。目前,我们只能使用这一种方法来使程序自动连接自定义容器内部的数据库,因为加载原理如下:
项目启动后,通过SpringFactoriesLoader.load方法获取到ConnectionDetailsFactory类的实例,也就是下面spring.factories配置文件中的实例,一共18个,但是最后一个由于没有相关依赖无法加载,所以是17个。
# Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.docker.compose.lifecycle.DockerComposeListener,\ org.springframework.boot.docker.compose.service.connection.DockerComposeServiceConnectionsApplicationListener # Connection Details Factories org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactory=\ org.springframework.boot.docker.compose.service.connection.cassandra.CassandraDockerComposeConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.elasticsearch.ElasticsearchDockerComposeConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.flyway.JdbcAdaptingFlywayConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.liquibase.JdbcAdaptingLiquibaseConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.mariadb.MariaDbJdbcDockerComposeConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.mariadb.MariaDbR2dbcDockerComposeConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.mongo.MongoDockerComposeConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.mysql.MySqlJdbcDockerComposeConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.mysql.MySqlR2dbcDockerComposeConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.oracle.OracleJdbcDockerComposeConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.oracle.OracleR2dbcDockerComposeConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.postgres.PostgresJdbcDockerComposeConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.postgres.PostgresR2dbcDockerComposeConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.rabbit.RabbitDockerComposeConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.redis.RedisDockerComposeConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.sqlserver.SqlServerJdbcDockerComposeConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.sqlserver.SqlServerR2dbcDockerComposeConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.zipkin.ZipkinDockerComposeConnectionDetailsFactory
对compose.yaml文件中services节点下的所有直接子节点(也就是每一个容器)进行遍历,然后将每一个容器配置作为参数,使用上一个步骤中获取到的实例进行构建ConnectionDetails对象,无法构建则返回null。最终的数据库连接就是在ConnectionDetails实例内部。
因此,我们暂时无法自定义ConnectionDetails来扩展springboot的逻辑,但是我们可以借鉴他的底层代码,自行重写。
我们也可以同时启动多个数据库容器:
services:
db1:
image: mysql:latest
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: test_db
ports:
- "3306"
labels:
org.springframework.boot.service-connection: mysql #标识容器类型,比如mysql,redis等等
db2:
image: mysql:latest
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: test_db
ports:
- "3306"
labels:
org.springframework.boot.ignore: true #不连接该容器
如果启动多个MySQL数据库,springboot程序并不知道应该连接哪一个,所以会报错。但是我们可以在compose.yaml文件中通过配置忽略该数据库的连接。
org.springframework.boot.ignore: true
这条命令也给了我们一些其他的用法,比如说我要在启动springboot项目时自动启动一个非数据库类型的容器,并且在关闭项目时进行销毁。例如,我们使用RocketMq时,自动启动一个dashboard容器等等。
三、进行测试
创建一个TestController类,内容如下:
@RequiredArgsConstructor
@RestController
public class TestController {
private final DataSource dataSource;
@GetMapping
public String index() {
if (dataSource != null) {
return dataSource.toString();
} else {
return "null";
}
}
}
然后我们启动项目,可以看见日志如下:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.1.1)
2023-06-24T09:40:16.363+08:00 INFO 10508 --- [ main] t.j.s.SpringbootDockerApplication : Starting SpringbootDockerApplication using Java 17.0.2 with PID 10508 (D:\Documents\IdeaProjects\springboot-docker\target\classes started by jq125 in D:\Documents\IdeaProjects\springboot-docker)
2023-06-24T09:40:16.367+08:00 INFO 10508 --- [ main] t.j.s.SpringbootDockerApplication : No active profile set, falling back to 1 default profile: "default"
2023-06-24T09:40:16.475+08:00 INFO 10508 --- [ main] .s.b.d.c.l.DockerComposeLifecycleManager : Using Docker Compose file 'D:\Documents\IdeaProjects\springboot-docker\compose.yaml'
2023-06-24T09:40:18.080+08:00 INFO 10508 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JDBC repositories in DEFAULT mode.
2023-06-24T09:40:18.093+08:00 INFO 10508 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 9 ms. Found 0 JDBC repository interfaces.
2023-06-24T09:40:18.454+08:00 INFO 10508 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2023-06-24T09:40:18.463+08:00 INFO 10508 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2023-06-24T09:40:18.463+08:00 INFO 10508 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.10]
2023-06-24T09:40:18.533+08:00 INFO 10508 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2023-06-24T09:40:18.533+08:00 INFO 10508 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 871 ms
2023-06-24T09:40:18.822+08:00 INFO 10508 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2023-06-24T09:40:19.113+08:00 INFO 10508 --- [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@6dd79214
2023-06-24T09:40:19.114+08:00 INFO 10508 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2023-06-24T09:40:19.206+08:00 INFO 10508 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2023-06-24T09:40:19.213+08:00 INFO 10508 --- [ main] t.j.s.SpringbootDockerApplication : Started SpringbootDockerApplication in 3.229 seconds (process running for 3.934)
从日志中,我们可以看见项目启动时初始化了HikariDataSource数据源。我们可以访问localhost:8080查看。
四、其他配置
springboot还提供了一些配置,可以让我们设置容器的生命周期,比如随着项目启动而启动,随着项目结束而销毁容器等等。我们还可以指定compose.yaml文件路径,甚至是结合springboot的多环境配置设置不同的compose.yaml文件路径。下面是几个配置用例:
spring:
profiles:
active: dev
docker:
compose:
lifecycle-management: start-and-stop
stop:
timeout: 1m
其他配置可以查阅官方文档,或者查看源码。
本文源码:springboot-docker: springboot3.1新特性,自动创建并连接容器内部数据库 (gitee.com)
评论区