首先是添加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
<version>${spring-boot.version}</version>
</dependency>
我这里用的SpringBoot版本是: 2.2.5.RELEASE
一、jedis与lettuce区别
- 1、Jedis: 在实现上是直连 redis server,多线程环境下非线程安全,除非使用连接池,为每个 redis实例增加 物理连接。
- 2、Lettuce: 是 一种可伸缩,线程安全,完全非阻塞的Redis客户端,多个线程可以共享一个RedisConnection,它利用Netty NIO 框架来高效地管理多个连接,从而提供了异步和同步数据访问方式,用于构建非阻塞的反应性应用程序。
我这里项目的配置文件使用的是 Lettuce
,配置文件内容:
spring:
datasource:
url: jdbc:mysql://192.168.104.64:3306/spring_boot_plus?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: huauN2021
# Redis配置
redis:
database: 0
host: 192.168.104.102
password: huauN@2021
port: 6379
timeout: 6000 # 连接超时时长(毫秒)
lettuce:
pool:
max-active: 1024 # 连接池最大连接数(默认为8,-1表示无限制 如果pool已经分配了超过max_active个jedis实例,则此时pool为耗尽)
max-wait: 10000 #最大等待连接时间,单位毫秒 默认为-1,表示永不超时,超时会抛出JedisConnectionException
max-idle: 10
min-idle: 5
首先是找下redis的配置类,如果没有则新增改配置类:
package gc.cnnvd.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* <p>
* Redis Template 配置
* </p>
*
* @author linmengmeng
* @date 2021-07-21
*/
@Configuration
public class RedisTemplateConfig {
/**
* redisTemplate配置
* @param factory
* @return
*
*
* 自定义RedisTemplate的原因:
* 1、修改泛型方式为<String,Object>,避免繁琐的类型转换
* 2、将value的序列化方式更改为Jackson2JsonRedisSerializer,因为底层的RedisSerializer序列化value时不会带双引号,而使用Jackson2JsonRedisSerializer序列化String类型时会自动添加双引号。
*
*/
@Bean
@SuppressWarnings("all")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory){
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
redisTemplate.setConnectionFactory(factory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper mapper = new ObjectMapper();
//指定要序列化的域,ANY是包括public和private的
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//指定序列化输入的类型,类必须是非final类型的,否则会报错
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(mapper);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
//设置key采用String的序列化方式
redisTemplate.setKeySerializer(stringRedisSerializer);
//设置hash的key采用String的序列化方式
redisTemplate.setHashKeySerializer(stringRedisSerializer);
//设置value采用jackson2的序列化方式
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
//设置hash的value采用jackson2的序列化方式
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
如果之前配置过 redisTemplate
,再次新增这个配置文件启动时就会提示异常,提示redisTemplate
bean创建失败,已存在什么的。
接着是 RedisCacheConfig
:
package gc.cnnvd.config;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
import java.util.Arrays;
/**
* <p>
* Redis Cache配置
* </p>
* @author geekidea
* @date 2018-11-08
*/
@Configuration
@EnableCaching
public class RedisCacheConfig extends CachingConfigurerSupport {
@Bean
@Override
public KeyGenerator keyGenerator() {
return (target, method, params) -> {
StringBuffer redisKey = new StringBuffer();
redisKey.append(target.getClass().getName()).append("-");
redisKey.append(method.getName());
if (params.length > 0) {
redisKey.append("-").append(Arrays.deepToString(params));
}
return redisKey.toString();
};
}
/**
* redis 缓存配置
* 修改序列化方式,解决缓存乱码
* @param factory
* @return
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
// 配置序列化
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
RedisCacheConfiguration redisCacheConfiguration = config
//Key序列化方式redisSerializer
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
//value序列化方式jackson2JsonRedisSerializer
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
//设置前缀
// .prefixKeysWith("project:")
//设置过期时间
.entryTtl(Duration.ofSeconds(60*60));
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
.cacheDefaults(redisCacheConfiguration)
.build();
return cacheManager;
}
}
我这里直接将注解@EnableScheduling
加在配置文件这里了,如果不加这里可以直接在启动类上要加上注解:@EnableScheduling
随便找了一个 service
方法进行测试:
@Override
@Cacheable(value = "adminUser",key = "#adminUserDetailParam.getId()",unless = "#result==null")
public AdminUserDetailVO getAdminUserDetail(AdminUserDetailParam adminUserDetailParam) {
AdminUserMerger adminUserMerger = adminUserMapper.selectAdminUserDetail(adminUserDetailParam.getId(), adminUserDetailParam.getVersion());
log.info("------------走数据库查询-------------");
if (adminUserMerger == null) {
throw new BusinessException("用户信息不存在");
}
AdminUserDetailVO adminUserDetailVO = new AdminUserDetailVO();
BeanCopierUtils.copyProperties(adminUserMerger, adminUserDetailVO);
return adminUserDetailVO;
}
这里仅在原来基础上加了一行注解@Cacheable(value = "adminUser",key = "#adminUserDetailParam.getId()",unless = "#result==null")
其中value为redis的key的前缀,
import com.alibaba.fastjson.JSONObject;
import gc.cnnvd.system.adminuser.param.AdminUserDetailParam;
import gc.cnnvd.system.adminuser.service.AdminUserService;
import gc.cnnvd.system.adminuser.vo.AdminUserDetailVO;
import gc.cnnvd.test.BaseTest;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
/**
* @Auther linmengmeng
* @Date 2021-07-20 19:15
*/
public class QueryTest extends BaseTest {
@Autowired
private AdminUserService adminUserService;
@Test
public void testGetAdminDetail(){
AdminUserDetailParam adminUserDetailParam = new AdminUserDetailParam();
adminUserDetailParam.setId("test001");
adminUserDetailParam.setVersion(0);
AdminUserDetailVO adminUserDetail = adminUserService.getAdminUserDetail(adminUserDetailParam);
System.out.println(JSONObject.toJSONString(adminUserDetail));
}
}
写了测试用例后,运行发现缓存好使了,第一次会走数据库查询,第二次直接走redis缓存。
缓存内容,使用可视化工具查看如下图: