1-添加maven依赖

2-添加redis配置

3-工具类

1-添加maven依赖

实际上是封装了jedis

        <!-- redis 依赖-->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.8.7.RELEASE</version>
</dependency>

2-添加redis配置

# REDIS (RedisProperties)
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=1024
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=10000
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=200
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=50
# 连接超时时间(毫秒)
spring.redis.timeout=10000
#哨兵监听redies server
spring.redis.sentinel.master=mymaster
#哨兵的配置列表
spring.redis.sentinel.nodes=192.168.2.228:26379,192.168.2.236:26379,192.168.2.237:26379

3-封装工具类

import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;
import com.google.gson.Gson;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils; /**
* Redis工具类
*/
@Component
public class RedisUtils { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired
private RedisTemplate redisTemplate;
@Resource(name = "redisTemplate")
private ValueOperations<String, String> valueOperations; /**
* 默认过期时长,单位:秒
*/
public final static long DEFAULT_EXPIRE = 60 * 60 * 24;
/**
* 加锁过期时长,单位:秒
*/
public final static long LOCK_EXPIRE = 60 * 60;
/**
* 不设置过期时长
*/
public final static long NOT_EXPIRE = -1;
private final static Gson GSON = new Gson(); //=============================common============================ /**
* 指定缓存失效时间
*
* @param key 键
* @param time 时间(秒)
*/
public boolean expire(CtimsModelEnum modelEnum, String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(formatKey(modelEnum, key), time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
logger.error("redis 操作失败,失败原因:", e);
e.printStackTrace();
return false;
}
} /**
* 根据key 获取过期时间
*
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(CtimsModelEnum modelEnum, String key) {
return redisTemplate.getExpire(formatKey(modelEnum, key), TimeUnit.SECONDS);
} /**
* 判断key是否存在
*
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(CtimsModelEnum modelEnum, String key) {
try {
return redisTemplate.hasKey(formatKey(modelEnum, key));
} catch (Exception e) {
logger.error("redis 操作失败,失败原因:", e);
e.printStackTrace();
return false;
}
} /**
* 删除缓存
*
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void del(CtimsModelEnum modelEnum, String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(formatKey(modelEnum, key[0]));
} else {
redisTemplate.delete(formatKey(modelEnum, key));
}
}
} //============================String============================= /**
* 普通缓存放入并设置时间
*
* @param key 键
* @param value 值
* @param expire 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public void set(CtimsModelEnum modelEnum, String key, Object value, long expire) {
if(expire != NOT_EXPIRE){
valueOperations.set(formatKey(modelEnum, key), toJson(value),expire,TimeUnit.SECONDS);
}else{
valueOperations.set(formatKey(modelEnum, key), toJson(value));
}
// if (expire != NOT_EXPIRE) {
// redisTemplate.expire(formatKey(modelEnum, key), expire, TimeUnit.SECONDS);
// }
} /**
* 普通缓存放入
*
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public void set(CtimsModelEnum modelEnum, String key, Object value) {
set(modelEnum, key, value, NOT_EXPIRE);
} public <T> T get(CtimsModelEnum modelEnum, String key, Class<T> clazz, long expire) {
String value = valueOperations.get(formatKey(modelEnum, key));
if (expire != NOT_EXPIRE) {
redisTemplate.expire(formatKey(modelEnum, key), expire, TimeUnit.SECONDS);
}
return value == null ? null : fromJson(value, clazz);
} /**
* 普通Model缓存获取
*
* @param key 键,clazz
* @return T 值
*/
public <T> T get(CtimsModelEnum modelEnum, String key, Class<T> clazz) {
return get(modelEnum, key, clazz, NOT_EXPIRE);
} /**
* 普通string缓存获取
*
* @param key 键,expire 更新缓存失效时间
* @return T 值
*/
public String get(CtimsModelEnum modelEnum, String key, long expire) {
String value = valueOperations.get(formatKey(modelEnum, key));
if (expire != NOT_EXPIRE) {
redisTemplate.expire(formatKey(modelEnum, key), expire, TimeUnit.SECONDS);
}
return value;
} /**
* 普通缓存获取
*
* @param key 键,clazz
* @return string 值
*/
public String get(CtimsModelEnum modelEnum, String key) {
return get(modelEnum, key, NOT_EXPIRE);
} /**
* 递增
*
* @param key 键
* @param delta 要增加几(大于0)
*/
public long incr(CtimsModelEnum modelEnum, String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(formatKey(modelEnum, key), delta);
} /**
* 递减
*
* @param key 键
* @param delta 要减少几(小于0)
*/
public long decr(CtimsModelEnum modelEnum, String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(formatKey(modelEnum, key), -delta);
} //================================Map================================= /**
* HashGet
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return 值
*/
public Object hget(CtimsModelEnum modelEnum, String key, String item) {
return redisTemplate.opsForHash().get(formatKey(modelEnum, key), item);
} /**
* HashGet
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return 值
*/
public <T> T hget(CtimsModelEnum modelEnum, String key, String item, Class<T> clazz) {
String value = (String) redisTemplate.opsForHash().get(formatKey(modelEnum, key), item);
return value == null ? null : fromJson(value, clazz); } /**
* 获取hashKey对应的所有键值
*
* @param key 键
* @return 对应的多个键值
*/
public Map<Object, Object> hmget(CtimsModelEnum modelEnum, String key) {
return redisTemplate.opsForHash().entries(formatKey(modelEnum, key));
} /**
* HashSet
*
* @param key 键
* @param map 对应多个键值
* @return true 成功 false 失败
*/
public boolean hmset(CtimsModelEnum modelEnum, String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(formatKey(modelEnum, key), map);
return true;
} catch (Exception e) {
logger.error("redis 操作失败,失败原因:", e);
e.printStackTrace();
return false;
}
} /**
* HashSet 并设置时间
*
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
* @return true成功 false失败
*/
public boolean hmset(CtimsModelEnum modelEnum, String key, Map<String, Object> map, long time) {
try {
redisTemplate.opsForHash().putAll(formatKey(modelEnum, key), map);
if (time > 0) {
expire(modelEnum, key, time);
}
return true;
} catch (Exception e) {
logger.error("redis 操作失败,失败原因:", e);
e.printStackTrace();
return false;
}
} /**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @return true 成功 false失败
*/
public boolean hset(CtimsModelEnum modelEnum, String key, String item, Object value) {
try {
int cs = 1; // 向Redis中入数据的次数
while(true){
redisTemplate.opsForHash().put(formatKey(modelEnum, key), item, value);
if(cs > 3 || hasKey(modelEnum, key)){
break;
}else{
cs ++;
}
}
if(cs > 3){
throw new Exception("向Redis插入数据失败,请稍后再试");
}
return true;
} catch (Exception e) {
logger.error("redis 操作失败,失败原因:", e);
e.printStackTrace();
return false;
}
} /**
* 向一张hash表中放入数据,如果不存在将创建
* 并指定缓存失效时间
*
* @param key 键
* @param item 项
* @param value 值
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(CtimsModelEnum modelEnum, String key, String item, Object value,
long time) {
try {
int cs = 1; // 向Redis中入数据的次数
while(true){
redisTemplate.opsForHash().put(formatKey(modelEnum, key), item, value);
// 如果缓存失效时间大于0,则指定缓存失效时间
if (time > 0) {
expire(modelEnum, key, time);
}
if(cs > 3 || hasKey(modelEnum, key)){
break;
}else{
cs ++;
}
}
if(cs > 3){
throw new Exception("向Redis插入数据失败,请稍后再试");
}
return true;
} catch (Exception e) {
logger.error("redis 操作失败,失败原因:", e);
e.printStackTrace();
return false;
}
} /**
* 删除hash表中的值
*
* @param key 键 不能为null
* @param item 项 可以使多个 不能为null
*/
public void hdel(CtimsModelEnum modelEnum, String key, Object... item) {
redisTemplate.opsForHash().delete(formatKey(modelEnum, key), item);
} /**
* 判断hash表中是否有该项的值
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(CtimsModelEnum modelEnum, String key, String item) {
return redisTemplate.opsForHash().hasKey(formatKey(modelEnum, key), item);
} /**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
*
* @param key 键
* @param item 项
* @param by 要增加几(大于0)
*/
public double hincr(CtimsModelEnum modelEnum, String key, String item, double by) {
return redisTemplate.opsForHash().increment(formatKey(modelEnum, key), item, by);
} /**
* hash递减
*
* @param key 键
* @param item 项
* @param by 要减少记(小于0)
*/
public double hdecr(CtimsModelEnum modelEnum, String key, String item, double by) {
return redisTemplate.opsForHash().increment(formatKey(modelEnum, key), item, -by);
} //============================set============================= /**
* 根据key获取Set中的所有值
*
* @param key 键
*/
public Set<Object> sGet(CtimsModelEnum modelEnum, String key) {
try {
return redisTemplate.opsForSet().members(formatKey(modelEnum, key));
} catch (Exception e) {
logger.error("redis 操作失败,失败原因:", e);
e.printStackTrace();
return null;
}
} /**
* 根据value从一个set中查询,是否存在
*
* @param key 键
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(CtimsModelEnum modelEnum, String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(formatKey(modelEnum, key), value);
} catch (Exception e) {
logger.error("redis 操作失败,失败原因:", e);
e.printStackTrace();
return false;
}
} /**
* 将数据放入set缓存
*
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSet(CtimsModelEnum modelEnum, String key, Object... values) {
try {
return redisTemplate.opsForSet().add(formatKey(modelEnum, key), values);
} catch (Exception e) {
logger.error("redis 操作失败,失败原因:", e);
e.printStackTrace();
return 0;
}
} /**
* 将set数据放入缓存
*
* @param key 键
* @param time 时间(秒)
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSetAndTime(CtimsModelEnum modelEnum, String key, long time, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(formatKey(modelEnum, key), values);
if (time > 0) {
expire(modelEnum, key, time);
}
return count;
} catch (Exception e) {
logger.error("redis 操作失败,失败原因:", e);
e.printStackTrace();
return 0;
}
} /**
* 获取set缓存的长度
*
* @param key 键
*/
public long sGetSetSize(CtimsModelEnum modelEnum, String key) {
try {
return redisTemplate.opsForSet().size(formatKey(modelEnum, key));
} catch (Exception e) {
logger.error("redis 操作失败,失败原因:", e);
e.printStackTrace();
return 0;
}
} /**
* 移除值为value的
*
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setRemove(CtimsModelEnum modelEnum, String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(formatKey(modelEnum, key), values);
return count;
} catch (Exception e) {
logger.error("redis 操作失败,失败原因:", e);
e.printStackTrace();
return 0;
}
}
//===============================list================================= /**
* 获取list缓存的内容
*
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
*/
public List<Object> lGet(CtimsModelEnum modelEnum, String key, long start, long end) {
try {
return redisTemplate.opsForList().range(formatKey(modelEnum, key), start, end);
} catch (Exception e) {
logger.error("redis 操作失败,失败原因:", e);
e.printStackTrace();
return null;
}
} /**
* 获取list缓存的长度
*
* @param key 键
*/
public long lGetListSize(CtimsModelEnum modelEnum, String key) {
try {
return redisTemplate.opsForList().size(formatKey(modelEnum, key));
} catch (Exception e) {
logger.error("redis 操作失败,失败原因:", e);
e.printStackTrace();
return 0;
}
} /**
* 通过索引 获取list中的值
*
* @param key 键
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
*/
public Object lGetIndex(CtimsModelEnum modelEnum, String key, long index) {
try {
return redisTemplate.opsForList().index(formatKey(modelEnum, key), index);
} catch (Exception e) {
logger.error("redis 操作失败,失败原因:", e);
e.printStackTrace();
return null;
}
} /**
* 将list放入缓存
*
* @param key 键
* @param value 值
*/
public boolean lSet(CtimsModelEnum modelEnum, String key, Object value) {
try {
redisTemplate.opsForList().rightPush(formatKey(modelEnum, key), value);
return true;
} catch (Exception e) {
logger.error("redis 操作失败,失败原因:", e);
e.printStackTrace();
return false;
}
} /**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
*/
public boolean lSet(CtimsModelEnum modelEnum, String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(formatKey(modelEnum, key), value);
if (time > 0) {
expire(modelEnum, key, time);
}
return true;
} catch (Exception e) {
logger.error("redis 操作失败,失败原因:", e);
e.printStackTrace();
return false;
}
} /**
* 将list放入缓存
*
* @param key 键
* @param value 值
*/
public boolean lSet(CtimsModelEnum modelEnum, String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(formatKey(modelEnum, key), value);
return true;
} catch (Exception e) {
logger.error("redis 操作失败,失败原因:", e); e.printStackTrace();
return false;
}
} /**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
*/
public boolean lSet(CtimsModelEnum modelEnum, String key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(formatKey(modelEnum, key), value);
if (time > 0) {
expire(modelEnum, key, time);
}
return true;
} catch (Exception e) {
logger.error("redis 操作失败,失败原因:", e);
e.printStackTrace();
return false;
}
} /**
* 根据索引修改list中的某条数据
*
* @param key 键
* @param index 索引
* @param value 值
*/
public boolean lUpdateIndex(CtimsModelEnum modelEnum, String key, long index, Object value) {
try {
redisTemplate.opsForList().set(formatKey(modelEnum, key), index, value);
return true;
} catch (Exception e) {
logger.error("redis 操作失败,失败原因:", e);
e.printStackTrace();
return false;
}
} /**
* 移除N个值为value
*
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lRemove(CtimsModelEnum modelEnum, String key, long count, Object value) {
try {
Long remove = redisTemplate.opsForList()
.remove(formatKey(modelEnum, key), count, value);
return remove;
} catch (Exception e) {
logger.error("redis 操作失败,失败原因:", e);
e.printStackTrace();
return 0;
}
} /**
* Object转成JSON数据
*/
private String toJson(Object object) {
if (object instanceof Integer || object instanceof Long || object instanceof Float ||
object instanceof Double || object instanceof Boolean || object instanceof String) {
return String.valueOf(object);
}
return GSON.toJson(object);
} /**
* JSON数据,转成Object
*/
private <T> T fromJson(String json, Class<T> clazz) {
return GSON.fromJson(json, clazz);
} /**
* Function: redis关键字生成<br/>
*
* @author luxiangxing<br/>
* @date 2018/6/27 下午8:12<br/>
* @param<br/>
* @return<br/>
* @version
*/
private String formatKey(CtimsModelEnum modelEnum, String key) {
return "ctims:" + modelEnum.getCode() + ":" + key;
} private List<String> formatKey(CtimsModelEnum modelEnum, String... key) {
List<String> list = new ArrayList<>();
for (String k : key) {
list.add(formatKey(modelEnum, k));
}
return list;
} /**
* 加锁
*
* @param value 当前时间+超时时间
*/
public boolean lock(String key, String value) {
//SETNX命令, 可以设置返回true, 不可以返回false
if (redisTemplate.opsForValue().setIfAbsent(key, value)) {
return true;
}
String currentValue = (String) redisTemplate.opsForValue().get(key);
//如果锁过期
if (!StringUtils.isEmpty(currentValue)
&& (Long.parseLong(currentValue) < System.currentTimeMillis())) {
//GETSET命令, 获取上一个锁的时间
String oldValue = (String) redisTemplate.opsForValue().getAndSet(key, value);
if (!StringUtils.isEmpty(oldValue) && oldValue.equals(value)) {
return true;
}
}
return false;
} /**
* 解锁
*/
public void unLock(String key, String value) {
try {
String currentValue = (String) redisTemplate.opsForValue().get(key);
if (!StringUtils.isEmpty(currentValue)
&& currentValue.equals(value)) {
redisTemplate.opsForValue().getOperations().delete(key);
}
} catch (Exception e) {
logger.error("【redis分布式锁】解锁异常, {}", e);
}
} /**
* 批量向redis中插入:key value
* 如果键已存在则返回false,不更新,防止覆盖。使用pipeline批处理方式(不关注返回值)
* @param list 一个map代表一行记录,2个key:key & value。
* @param ctimsModelEnum redis中key的值前缀。
* @return
*/
public boolean pipelinedString(final List<Map<String, Object>> list,
final CtimsModelEnum ctimsModelEnum) {
boolean result = (boolean) redisTemplate.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection connection)
throws DataAccessException {
RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
for (Map<String, Object> map : list) {
byte[] key = serializer
.serialize(formatKey(ctimsModelEnum, map.get("key").toString()));
byte[] values = serializer.serialize(map.get("value").toString());
connection.set(key, values);
}
return true;
}
}, false, true);
return result;
}
/**
* 批量向redis中插入:key value
* 如果键已存在则返回false,不更新,防止覆盖。使用pipeline批处理方式(不关注返回值)
* @param list 一个map代表一行记录,2个key:key & value。
* @param ctimsModelEnum redis中key的值前缀。
* @return
*/
public boolean pipelinedHash(final List<Map<String, Object>> list,
final CtimsModelEnum ctimsModelEnum) {
boolean result = (boolean) redisTemplate.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection connection)
throws DataAccessException {
RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
for (Map<String, Object> map : list) {
byte[] key = serializer
.serialize(formatKey(ctimsModelEnum, map.get("key").toString()));
byte[] hkey = serializer.serialize(map.get("hkey").toString());
byte[] values = serializer.serialize(map.get("value").toString());
connection.hSet(key, hkey, values);
}
return true;
}
}, false, true);
return result;
} /***
* 模糊搜索key值是否存在,spring-redis 版本号在1.8之后的不需要关闭游标,之前的需要关闭游标。
* @param modelEnum
* @param key
* @param count
* @return
*/
public boolean scan(CtimsModelEnum modelEnum, String key, long count) throws Exception {
RedisConnection redisConnection = redisTemplate.getConnectionFactory().getConnection();
Cursor cursor = redisConnection
.scan(ScanOptions.scanOptions().match(formatKey(modelEnum, key)).count(count).build());
try {
Boolean isHas = cursor.hasNext();
return isHas;
} catch (Exception e) {
logger.error("redis 查询key是否存在 异常:" + formatKey(modelEnum, key), e);
return false;
} finally {
if (null != cursor) {
cursor.close();
}
}
} }
import org.springframework.util.StringUtils;

public enum CtimsModelEnum {
CTIMS_COMM_CAP("comm", "公共"); private String code;
private String desc; public static CtimsModelEnum getByCode(String code) {
if (StringUtils.isEmpty(code)) {
return null;
} else {
CtimsModelEnum[] var1 = values();
int var2 = var1.length; for(int var3 = 0; var3 < var2; ++var3) {
CtimsModelEnum ctimsModelEnum = var1[var3];
if (ctimsModelEnum.getCode().equals(code)) {
return ctimsModelEnum;
}
} return null;
}
} private CtimsModelEnum(String code, String desc) {
this.code = code;
this.desc = desc;
} public String getCode() {
return this.code;
} public void setCode(String code) {
this.code = code;
} public String getDesc() {
return this.desc;
} public void setDesc(String desc) {
this.desc = desc;
}
}

最新文章

  1. Sphinx和coreseek检索引擎
  2. Sql Server 基础知识
  3. 10个你必须掌握的超酷VI命令技巧
  4. [ES6] ITERATORS
  5. [poco] HttpRequest之post方法
  6. KTHREAD 线程调度 SDT TEB SEH shellcode中DLL模块机制动态
  7. Python 一些常用模块的安装
  8. ROS_Kinetic_08 ROS的集成开发环境(IDEs)之使用Eclipse
  9. Vue系列之 =&gt; 使用webpack-dev-server工具实现自动打包编译
  10. express+websocket+exec+spawn=webshell
  11. CSS多行文本垂直居中
  12. Nunit单元测试入门学习随笔(一)
  13. js 判断浏览器型号
  14. 关于SQL视图的创建和使用方法
  15. AJAX理解
  16. 一个按成绩排序SQL的写法问题
  17. 如何在 Linux 上设置密码策略
  18. utf-8编码的csv文件,用excel打开乱码,解决办法,在输出前加 0xEF,0xBB,0xBF三个char
  19. 西南交通大学结构服役安全及力学基础创新团队在Wiley出版英文专著(转载)
  20. Dijkstra算法求最短路径(java)(转)

热门文章

  1. E. Sergey and Subway
  2. python学习之-- 生成唯一ID
  3. Oracle服务扫描工具Oscanner
  4. UVA - 10615 Rooks
  5. SPOJ 8222 Substrings
  6. Div 浮动到另一个div之上
  7. dubbo常见问题解答FAQ
  8. Android硬件抽象层(HAL)深入剖析(二)
  9. 第四讲_图像识别之图像分类Image Classification
  10. 22. Spring Boot 拦截器HandlerInterceptor【从零开始学Spring Boot】