目录
- 前言
- pom引入
- RedisCacheConfig配置
- RedisUtil 工具类
- application.yml配置
- 用户详情查询测试
项目目录结构
前言
关于Redis的解释网上有很多,可以自行查阅,Redis与SpringBoot整合有两种方式,第一种是使用Jedis,它是Redis官方推荐的面向Java的操作Redis的客户端,第二种是使用RedisTemplate,它是SpringDataRedis中对JedisApi的高度封装。我此次使用的是RedisTemplate,并整理了redis工具类方便大家使用,GitHub地址文末给出,需要的话可以留言,留下QQ邮箱号,我会发给你。
POM引入
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
RedisCacheConfig配置
自定义序列化器Jackson2JsonRedisSerializer是解决序列号乱码问题
package com.example.demo.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* @ClassName: RedisCacheConfig
* @Description: redis 缓存配置;
* 注意:RedisCacheConfig这里也可以不用继承:CachingConfigurerSupport,
* 也就是直接一个普通的Class就好了 这里主要我们之后要重新实现
* key的生成策略,只要这里修改KeyGenerator,其它位置不用修改就生效了。
* 普通使用普通类的方式的话,那么在使用@Cacheable的时候还需要指定KeyGenerator的名称;
* 这样编码的时候比较麻烦。
* @author: lst
* @date: 2019年12月25日 下午3:30:19
*/
@Configuration
@EnableCaching // 启用缓存,这个注解很重要;
public class RedisCacheConfig {
@Autowired
private RedisConnectionFactory redisConnectionFactory;
/**
* 缓存配置初始化一个cacheManager
* @param connectionFactory
* @return
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheManager redisCacheManager = RedisCacheManager.builder(connectionFactory).build();
return redisCacheManager;
}
/**
* 防止redis入库序列化乱码的问题
* @param redisConnectionFactory
* @return RedisTemplate
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 使用Jackson2JsonRedisSerialize 替换默认序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
// 设置value的序列化规则和 key的序列化规则
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new StringRedisSerializer());
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
/**
* 重写hashOperations
* @param redisTemplate
* @return
*/
@Bean
public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForHash();
}
/**
* 重写listOperations
* @param redisTemplate
* @return
*/
@Bean
public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForList();
}
/**
* redisMessageListenerContainer
* @return
*/
@Bean
public RedisMessageListenerContainer redisMessageListenerContainer() {
RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();
redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory);
return redisMessageListenerContainer;
}
}
RedisUtil 工具类
package com.example.demo.utils;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* @author LST
* @version 1.0
* @Description: redis工具类
* @date 2019-12-25 16:52
*/
@Component
public class RedisUtil<T> {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private HashOperations<String, String, Object> hashOperations;
@Autowired
private ListOperations<String, Object> listOperations;
/**
* 默认过期时长,单位:秒/ 三十分钟
*/
public final static long DEFAULT_EXPIRE = 180 * 10;
/**
* 不设置过期时长
*/
public final static long NOT_EXPIRE = -1;
/**
* 普通缓存放入
* @param key 键
* @param value 值
*/
public void setValue(String key, T value) {
redisTemplate.opsForValue().set(key, value);
}
/**
* 普通缓存放入并设置时间
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
*/
public void setValue(String key, T value, Long time) {
setValue(key, value);
if(time > 0){
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
}
/**
* 普通缓存放入并设置时间和单位
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @param unit 缓存设置的时间单位
*/
public void setValue(String key, T value, Long time, TimeUnit unit) {
setValue(key, value);
redisTemplate.expire(key, time, unit);
}
/**
* 普通缓存获取
* @param key 键
* @return 值
*/
public T getValue(String key) {
ValueOperations<String, T> valueOperations = redisTemplate.opsForValue();
return valueOperations.get(key);
}
/**
* 删除缓存
* @param key 键
*/
public void deleteValue(String key) {
redisTemplate.delete(key);
}
/**
* 判断key是否存在
* @param key 键
* @return true 存在 false 不存在
*/
public boolean exists(String key) {
if (getValue(key) == null) {
return false;
} else {
return true;
}
}
/**
* 放入一个map对象
* @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) {
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) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* Map缓存获取
* @param key 键
* @return map
*/
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* 重复提交
* @param key 键
* @return
*/
public long repeatSubmit(String key) {
ValueOperations<String, Integer> valueOperations = redisTemplate.opsForValue();
long ret = valueOperations.increment(key, 1);
redisTemplate.expire(key, 10L, TimeUnit.SECONDS);
return ret;
}
/**
* 将list放入缓存
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
return false;
}
}
/**
* 获取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) {
return null;
}
}
/**
* 删除hash值
*
* @param key key
* @param hashKeys hashKeys
*/
public void delHash(String key, String... hashKeys) {
Arrays.stream(hashKeys).forEach(hashKey -> hashOperations.delete(key, hashKeys));
}
/**
* 先根据key删除
* 然后在把list放入缓存中
*
* @param key key
* @param getHashKeyFunction 获取hashKey值的方法
* @param list list实体类
* @param expire 时间
*/
public <T> void putHashList(String key, Function<T, String> getHashKeyFunction, List<T> list, Long expire) {
putHashList(key, getHashKeyFunction, list);
if (expire != NOT_EXPIRE) {
redisTemplate.expire(key, expire, TimeUnit.SECONDS);
}
}
/**
* 先根据key删除
* 然后在把list放入缓存中
* @param key 键
* @param getHashKeyFunction
* @param list
* @param <T>
*/
public <T> void putHashList(String key, Function<T, String> getHashKeyFunction, List<T> list) {
deleteValue(key);
list.stream().filter(distinctByValue(getHashKeyFunction)).forEach(model -> putHashModel(key, getHashKeyFunction, model));
}
/**
* 将实体类放到Hash里面,并设置时间
* @param key key
* @param getHashKeyFunction 获取hashKey值的方法
* @param model 实体类
* @param <T> T
*/
public <T> void putHashModel(String key, Function<T, String> getHashKeyFunction, T model, Long expire) {
putHashModel(key, getHashKeyFunction, model);
if (expire != NOT_EXPIRE) {
redisTemplate.expire(key, expire, TimeUnit.SECONDS);
}
}
/**
* 将实体类放到Hash里面
* @param key key
* @param getHashKeyFunction 获取hashKey值的方法
* @param model 实体类
* @param <T> T
*/
public <T> void putHashModel(String key, Function<T, String> getHashKeyFunction, T model) {
hashOperations.put(key, getHashKeyFunction.apply(model), JSONObject.toJSON(model).toString());
}
/**
* 根据有效时间,key 获取List
*
* @param key key
* @param clazz clazz
* @param <T> T
* @return List<T>
*/
public <T> List<T> getHashList(String key, Class<T> clazz, Long expire) {
if (expire != NOT_EXPIRE) {
redisTemplate.expire(key, expire, TimeUnit.SECONDS);
}
return getHashList(key, clazz);
}
/**
* 获取全部list
* @param key
* @param clazz
* @param <T>
* @return
*/
public <T> List<T> getHashList(String key, Class<T> clazz) {
Optional<List<Object>> objectList = Optional.ofNullable(hashOperations.values(key));
return objectList.map(objects -> objects.stream()
.map(t -> JSONObject.parseObject(JSONObject.toJSON(t).toString(), clazz))
.collect(Collectors.toList())).orElse(null);
}
/**
* 根据 key 和 hashKey 获取值
*
* @param key key
* @param hashKey hashKey
* @param clazz clazz
* @param <T> T
* @return T
*/
public <T> T getHashObject(String key, String hashKey, Class<T> clazz) {
Optional<Object> object = Optional.ofNullable(hashOperations.get(key, hashKey));
if (object.isPresent()) {
return JSONObject.parseObject(JSONObject.toJSON(object).toString(), clazz);
}
return null;
}
/**
* 去除重复
* @param keyExtractor
* @param <T>
* @return
*/
public static <T> Predicate<T> distinctByValue(Function<? super T, ?> keyExtractor) {
ConcurrentHashMap<Object, Boolean> map = new ConcurrentHashMap<>();
return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
/**
* 判断key是否存在
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
return false;
}
}
/**
* 获取有效全部list
* @param key key
* @param clazz clazz
* @param <T> T
* @param expire 超时时间
* @return return
*/
public <T> List<T> getList(String key, Class<T> clazz, Long expire) {
if (expire != NOT_EXPIRE) {
redisTemplate.expire(key, expire, TimeUnit.SECONDS);
}
return getList(key, clazz);
}
/**
* 获取全部list
* @param key
* @param clazz
* @param <T>
* @return
*/
public <T> List<T> getList(String key, Class<T> clazz) {
Optional<List<Object>> list = Optional.ofNullable(listOperations.range(key, 0, listOperations.size(key)));
return list.map(objects -> objects.stream()
.map(t -> JSONObject.parseObject(JSONObject.toJSON(t).toString(), clazz))
.collect(Collectors.toList())).orElse(null);
}
/**
* 先根据key删除
* 然后在把list放入缓存中
* @param key 键
* @param getValueFunction 获取value值的方法
* @param list list
* @param <T> T
* @param expire 超时时间
*/
public <T> void leftPushList(String key, List<T> list, Function<T, String> getValueFunction, Long expire) {
leftPushList(key, list, getValueFunction);
if (expire != NOT_EXPIRE) {
redisTemplate.expire(key, expire, TimeUnit.SECONDS);
}
}
/**
*实体类去重放入listOperations
* @param key
* @param list
* @param getValueFunction 获取value值的方法
* @param <T>
*/
public <T> void leftPushList(String key, List<T> list, Function<T, String> getValueFunction) {
deleteValue(key);
list.stream().filter(distinctByValue(getValueFunction)).forEach(model -> leftPushModel(key, model, getValueFunction));
}
/**
* 实体类放入
* @param key 键
* @param model
* @param getValueFunction
* @param <T>
*/
public <T> void leftPushModel(String key, T model, Function<T, String> getValueFunction) {
listOperations.leftPush(key, getValueFunction.apply(model));
}
}
application.yml配置
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/test?autoReconnect=true&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
username: root
password: root1234
# 使用Druid数据源
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
druid:
filters: stat
maxActive: 20
initialSize: 1
maxWait: 60000
minIdle: 1
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxOpenPreparedStatements: 20
data:
redis:
repositories:
enabled: false
redis:
database: 0 # redis数据库索引(默认为0),我们使用索引为其他(0-15)的数据库,避免和其他数 据库冲突
host: 127.0.0.1
port: 6379
password: 12345678
用户详情查询测试
UserController类
/**
* 获取用户详情
* @param userId 用户id
* @return
*/
@GetMapping(value = "/{userId}", produces = "application/json; charset=utf-8")
@ApiOperation(value = "获取用户详情", notes = "获取用户详情", code = 200, produces = "application/json")
public RestResponse<IPage<UserVO>> getUser(@PathVariable("userId") String userId) {
return ResultGenerator.genSuccessResult(userService.getUser(userId));
}
UserServiceImpl实现类
/**
* 获取用户详情
* @param userId 用户id
* @return
*/
@Override
public User getUser(String userId) {
//先查询redis缓存
User user = (User) redisUtil.getValue(userId);
if(user == null){
user = userMapper.selectOne(new QueryWrapper<User>().lambda().eq(User::getId, userId));
if(user == null){
throw new SXException(ServiceExceptionEnum.DATA_NOT_REQUESTED);
}
//放入缓存中
redisUtil.setValue(userId, user);
}
return user;
}
每次查询会先查询redis里面有没有,没有的话,就查询数据库,然后放入redis缓存中,以后查询就直接查询redis中即可。
第一次查询:
第二次查询:
因为整合的其实不复杂,只要把本文章的RedisCacheConfig和RedisUtil拷贝进自己的项目可以直接使用。或者需要源码的小伙伴可直接留言。