前言有时候我们需要快速启动一些项目,但是环境往往折腾了好久,因此弄一个可以重用的快速搭建的教程, docker 简直就是这方面的神器,Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux 或 Windows 操作系统的机器上,也可以实现虚拟化。
本教程基于的前提条件:
机器已经安装配置好 JDK1.8 ,并且环境变量已经配置成功 Maven 已经配置好, IDEA 项目使用的默认方式 Maven 也配置成功本地机器安装好 Docker 顺便提一句,我用 navicat 作为数据库可视化操作工具项目地址:https://github.com/Damaer/DemoCode/tree/main/springboot/springdemo
项目目录├── src :源代码| ├── main| | ├── java| | | ├── com.aphysia.springbootdemo| | | | ├── config:配置| | | | | ├── RedisConfig:redis 配置| | | | ├── constant:常量| | | | | ├── RedisConfig:redis 常量| | | | ├── controller:控制器| | | | ├── mapper:数据库操作接口| | | | ├── model:实体类| | | | ├── service:逻辑处理层,包括接口以及实现类| | | | | ├── impl:接口实现类| | | | ├──util:工具类| | | | | ├── RedisUtil:redis 工具类| | | | ├──SpringdemoApplication:启动类| | ├── resource| | | ├── mapper 数据库操作 sql| | | ├── application.yml:全局配置类| | | ├── user.sql: 初始化 mysql| ├── test: 测试类 ├── pom.xml :项目 maven 依赖关系整体的目录如下:
如何基于 Docker 快速搭建 Springboot + Mysql + Redis 项目搭建项目
docker 安装启动 mysql 以及 redis1.1 安装 mysql 查询 mysql 最新的镜像:
docker search mysql 拉取最新的 mysql 版本
docker pull mysql:latest 启动 mysql ,用户名 root ,密码 123456
docker run -itd --name mysql-test -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql 可以通过 docker ps 查看是否安装成功
% docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES574d30f17868 mysql "docker-entrypoint.s…" 14 months ago Up 2 days 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp mysql-test 如何基于 Docker 快速搭建 Springboot + Mysql + Redis 项目 1.2 安装 redis 查询 redis 的镜像
docker search redis 拉取 redis 的最新镜像
% docker pull redis:latestlatest: Pulling from library/rediseff15d958d66: Pull complete1aca8391092b: Pull complete06e460b3ba1b: Pull completedef49df025c0: Pull complete646c72a19e83: Pull completedb2c789841df: Pull completeDigest: sha256:619af14d3a95c30759a1978da1b2ce375504f1af70ff9eea2a8e35febc45d747Status: Downloaded newer image for redis:latestdocker.io/library/redis:latestdocker images 可以查看我们安装了哪些镜像,可以看到其实我之前也安装过 redis 的镜像:
% docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEredis latest 40c68ed3a4d2 3 days ago 113MBredis <none> 84c5f6e03bf0 14 months ago 104MBmysql latest e1d7dc9731da 14 months ago 544MBdocker/getting-started latest 1f32459ef038 16 months ago 26.8MB 让我们启动一下 redis 的容器:
% docker run -itd --name redis-test -p 6379:6379 redis7267e14faf93a0e416c39eeaaf51705dc4b6dc3507a68733c20a2609ade6dcd6 可以看到 docker 里面现在有 redis 和 mysql 两个容器在跑了:
如何基于 Docker 快速搭建 Springboot + Mysql + Redis 项目 2. 初始化数据库主要是创建数据库以及测试使用的数据表,初始化数据库的语句:
drop database IF EXISTS test;CREATE DATABASE test;use test;DROP TABLE IF EXISTS user
;CREATE TABLE user
(id
int(11) NOT NULL,name
varchar(255) DEFAULT "",age
int(11) DEFAULT 0,PRIMARY KEY (id
)) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO user
VALUES (1, '李四', 11);INSERT INTO user
VALUES (2, '王五', 11);初始化数据如下:
如何基于 Docker 快速搭建 Springboot + Mysql + Redis 项目 3.创建项目在 IDEA 中,File --> New --> Project --> Spring Initializr(选择 JDK 8):
如何基于 Docker 快速搭建 Springboot + Mysql + Redis 项目点击 Next :
如何基于 Docker 快速搭建 Springboot + Mysql + Redis 项目选择 Web 下面的 Spring Web , SQL 下面的 JDBC API , Mybatis , NoSQL 下的 Redis ,也可以不选,直接在 pom 文件里自己加入即可:
如何基于 Docker 快速搭建 Springboot + Mysql + Redis 项目一路点 Next ,最后 Finish ,创建好之后,记得更新一下 Maven ,安装依赖包。
4.初始化代码 4.1 全局配置文件以及启动类全局配置文件 application.yml :
server:port: 8081spring:#数据库连接配置 datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf-8&useSSL=falseusername: rootpassword: 123456redis:host: 127.0.0.1 ## redis 所在的服务器 IPport: 6379##密码,我这里没有设置,所以不填 password:## 设置最大连接数,0 为无限 pool:max-active: 8min-idle: 0max-idle: 8max-wait: -1#mybatis 的相关配置 mybatis:#mapper 配置文件 mapper-locations: classpath:mapper/*.xmltype-aliases-package: com.aphysia.spingbootdemo.model#开启驼峰命名 configuration:map-underscore-to-camel-case: truelogging:level:root: debug 启动类 SpringdemoApplication :
import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.EnableAutoConfiguration;import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication@MapperScan("com.aphysia.springdemo.mapper")public class SpringdemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringdemoApplication.class, args);
}
复制代码
}4.2 实体类与数据库中 user 表对应的实体类 User.java :
package com.aphysia.springdemo.model;
public class User {int id;String name;int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
复制代码
}4.3 Redis 工具类 Redis 配置类 RedisConfig :
package com.aphysia.springdemo.config;
import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configurationpublic class RedisConfig {
@Autowired
private RedisTemplate redisTemplate;
@Bean
public RedisTemplate redisTemplateInit() {
//设置序列化Key的实例化对象
redisTemplate.setKeySerializer(new StringRedisSerializer());
//设置序列化Value的实例化对象
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return redisTemplate;
}
复制代码
}Redis 常量类 RedisConstant :
package com.aphysia.springdemo.constant;
public class RedisConstant {public static String ALL_USER_KEY = "allUser";}Redis 工具类 RedisUtil :
package com.aphysia.springdemo.util;
import java.util.Collection;import java.util.List;import java.util.Map;import java.util.Set;import java.util.concurrent.TimeUnit;
import org.springframework.data.redis.core.RedisTemplate;import org.springframework.stereotype.Component;import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
@Componentpublic class RedisUtil {@Resourceprivate RedisTemplate<String, Object> redisTemplate;
public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
/**
* 指定缓存失效时间
*
* @param key 键
* @param time 时间(秒)
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据key 获取过期时间
*
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判断key是否存在
*
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
*
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));
}
}
}
//============================String=============================
/**
* 普通缓存获取
*
* @param key 键
* @return 值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
*
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存放入并设置时间
*
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 递增
*
* @param key 键
* @param delta 要增加几(大于0)
* @return
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递减
*
* @param key 键
* @param delta 要减少几(小于0)
* @return
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
//================================Map=================================
/**
* HashGet
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return 值
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取hashKey对应的所有键值
*
* @param key 键
* @return 对应的多个键值
*/
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* HashSet
*
* @param key 键
* @param map 对应多个键值
* @return true 成功 false 失败
*/
public boolean hmset(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* HashSet 并设置时间
*
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
* @return true成功 false失败
*/
public boolean hmset(String key, Map<String, Object> map, long time) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value, long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除hash表中的值
*
* @param key 键 不能为null
* @param item 项 可以使多个 不能为null
*/
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}
/**
* 判断hash表中是否有该项的值
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
*
* @param key 键
* @param item 项
* @param by 要增加几(大于0)
* @return
*/
public double hincr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* hash递减
*
* @param key 键
* @param item 项
* @param by 要减少记(小于0)
* @return
*/
public double hdecr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
}
//============================set=============================
/**
* 根据key获取Set中的所有值
*
* @param key 键
* @return
*/
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 根据value从一个set中查询,是否存在
*
* @param key 键
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将数据放入set缓存
*
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 将set数据放入缓存
*
* @param key 键
* @param time 时间(秒)
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSetAndTime(String key, long time, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0) expire(key, time);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取set缓存的长度
*
* @param key 键
* @return
*/
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 移除值为value的
*
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
//===============================list=================================
/**
* 获取list缓存的内容
*
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
* @return
*/
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取list缓存的长度
*
* @param key 键
* @return
*/
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 通过索引 获取list中的值
*
* @param key 键
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
* @return
*/
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0) expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0) expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据索引修改list中的某条数据
*
* @param key 键
* @param index 索引
* @param value 值
* @return
*/
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 移除N个值为value
*
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lRemove(String key, long count, Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
复制代码
}4.4 Mysql 数据库操作数据库的 sql 文件 UserMapper.xml
<?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="com.aphysia.springdemo.mapper.UserMapper"><select id="getAllUsers" resultType="com.aphysia.springdemo.model.User">SELECT * FROM user</select>
<update id="updateUserAge" parameterType="java.lang.Integer">
update user set age=age+1 where id =#{id}
</update>
复制代码
</mapper>对应的 mapper 接口 UserMapper.java
package com.aphysia.springdemo.mapper;
import com.aphysia.springdemo.model.User;
import java.util.List;
public interface UserMapper {List<User> getAllUsers();
int updateUserAge(Integer id);
复制代码
}4.5 Service 层先定义一个操作 User 的接口类 UserService ,包含两个方法,查询所有的 user 以及更新 user 的年龄:
package com.aphysia.springdemo.service;
import com.aphysia.springdemo.model.User;
import java.util.List;
public interface UserService {public List<User> getAllUsers();
public void updateUserAge();
复制代码
}接口实现类 UserServiceImpl , 为了证实 Redis 可用,我们查询所有的用户的时候,加入了 Redis 缓存,优先从 Redis 中加载数据 :
package com.aphysia.springdemo.service.impl;
import com.aphysia.springdemo.constant.RedisConstant;import com.aphysia.springdemo.mapper.UserMapper;import com.aphysia.springdemo.model.User;import com.aphysia.springdemo.service.UserService;import com.aphysia.springdemo.util.RedisUtil;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;import java.util.List;
@Service("userService")public class UserServiceImpl implements UserService {
@Resource
UserMapper userMapper;
@Autowired
RedisUtil redisUtil;
@Override
public List<User> getAllUsers() {
List<User> users = (List<User>) redisUtil.get(RedisConstant.ALL_USER_KEY);
if(CollectionUtils.isEmpty(users)){
users = userMapper.getAllUsers();
redisUtil.set(RedisConstant.ALL_USER_KEY,users);
}
return users;
}
@Override
@Transactional
public void updateUserAge() {
redisUtil.del(RedisConstant.ALL_USER_KEY);
userMapper.updateUserAge(1);
userMapper.updateUserAge(2);
}
复制代码
}4.6 Controller 控制层增加一个测试层 TestController :
package com.aphysia.springdemo.controller;
import com.aphysia.springdemo.model.User;import com.aphysia.springdemo.service.UserService;import com.aphysia.springdemo.util.RedisUtil;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
@Controllerpublic class TestController {
@Autowired
UserService userService;
@RequestMapping("/getUserList")
@ResponseBody
public List<User> getUserList() {
return userService.getAllUsers();
}
@RequestMapping("/update")
@ResponseBody
public int update() {
userService.updateUserAge();
return 1;
}
复制代码
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--mysql数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
复制代码
</project>测试启动项目,输入链接:http://localhost:8081/getUserList,可以获取到所有的 user :
如何基于 Docker 快速搭建 Springboot + Mysql + Redis 项目我们更新一下所有的用户年龄,调用 http://localhost:8081/update ,返回 1
如何基于 Docker 快速搭建 Springboot + Mysql + Redis 项目再次访问 http://localhost:8081/getUserList ,可以看到年龄全部都变成 12 :
如何基于 Docker 快速搭建 Springboot + Mysql + Redis 项目怎么知道 Redis 生效了呢?最好就是 debug ,或者直接看控制台,我们已经开启了 debug 级别的日志:
如何基于 Docker 快速搭建 Springboot + Mysql + Redis 项目还有一种方式,下载 Redis-desktop-manager ,可以直接可视化查看:
如何基于 Docker 快速搭建 Springboot + Mysql + Redis 项目至此,一个 demo 项目就完成了,可以每次 copy 出来初始化使用。
以上就是今日分享啦!
评论