springboot整合redis
Posted FreeFly辉
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了springboot整合redis相关的知识,希望对你有一定的参考价值。
此文介绍的是redis整合springboot作为缓存的使用
如果你未接触redis,或想独立使用原生java api操作redis,可以到菜鸟教程先熟悉一下
建议耐心读完一下内容(至少学习第三方软件流程读完)
学习第三方软件流程
- 先要了解该第三方软件作用(例如你学mysql肯定先从概念学习)
- 使用第三方提供的原生指令进行操作联系(例如你学mysql,肯定是先学习基本的sql语句,然后控制台(或工具)进行sql敲写练习,熟悉基本操作)
- 寻找对应软件的java驱动包,然后利用原生javaapi进行操作(例如mysql对应的原生驱动)
- 最后是否有第三方框架,或工具包(例如连接池)等优化工具
以上这些过程菜鸟教程都可以给你教学(当然这只是我知道的网址,肯定有很多类似教程),以上所有流程你可以在2个小时内搞定(不用所有教程指令都敲一遍)
菜鸟教程redis网址当你做完以上工作,就可以尝试整合在springboot中使用了
引入jar包(你肯定会快速搭建启动一个springboot项目的)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<!-- lettuce连接客户端,基于netty的单线程并发连接,同功能的还有jedis,spring官网比较看着lettuce支持的功能多些-->
<!--lettuce和jedis就像是驱动框架,有各自的实现,spring redis像是jpa一样封装了统一api,不在意你用的哪种连接框架,spring提供的只是redis操作模板-->
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>6.1.4.RELEASE</version>
</dependency>
注入bean(以下代码全部加了注释)
@Configuration
@EnableCaching
public class RedisPoolNoProperties {
/**
*以下配置项我直接写在代码里方便观看,正常的配置应该写在properties或yml文件中
*然后再引入进来,可以用 spring的@Value注解,例如
* @Value("${spring.redis.database}")
* private int database;
*/
private int database = 0; //默认0
private String host = "127.0.0.1";//redis启动的机器IP地址,这里在同一台电脑启动的
private String password; //redis默认没有密码
private int port = 6379; //redis默认端口,启动时可以看到
private long timeout = 5000;//连接配置,可以发现无论什么网络连接都逃不出超时时间设置
private long shutDownTimeout = 5000;//断开连接超时时间
/**以下是连接池配置,可参考学习的mysql连接池配置,
* 可以看出所有的连接池 都会有 最大连接数,最小连接数,核心数等设置
* 更多配置项可自行查找文档
* */
private int maxIdle=50; //最大连接空闲数
private int minIdle=5;
private int maxActive=50;
private long maxWait=5000;
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
@Bean //注入连接工厂bean,这里用的lettuce,同样的工具还有jedis
public LettuceConnectionFactory lettuceConnectionFactory() {
GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
genericObjectPoolConfig.setMaxIdle(maxIdle);
genericObjectPoolConfig.setMinIdle(minIdle);
genericObjectPoolConfig.setMaxTotal(maxActive);
genericObjectPoolConfig.setMaxWaitMillis(maxWait);
genericObjectPoolConfig.setTimeBetweenEvictionRunsMillis(100);
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setDatabase(database);
redisStandaloneConfiguration.setHostName(host);
redisStandaloneConfiguration.setPort(port);
redisStandaloneConfiguration.setPassword(RedisPassword.of(password));
LettuceClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder()
.commandTimeout(Duration.ofMillis(timeout))
.shutdownTimeout(Duration.ofMillis(shutDownTimeout))
.poolConfig(genericObjectPoolConfig)
.build();
LettuceConnectionFactory factory = new LettuceConnectionFactory(redisStandaloneConfiguration, clientConfig);
return factory;
}
@Bean//通过连接工厂bean注入 readisTemplate,都是一些配置项
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(lettuceConnectionFactory);
//使用Jackson2JsonRedisSerializer替换默认的JdkSerializationRedisSerializer来序列化和反序列化redis的value值
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
jackson2JsonRedisSerializer.setObjectMapper(mapper);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
//key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
注入完以上bean后,你可以注入redisTemplate使用了,redisTemplate包含了所有操作
例如:
@RestController
@RequestMapping("string")
public class TestController {
@Autowired
RedisTemplate redisTemplate;
/**
* 通过redisTemplate的ops方法可以获得对应数据类型的操作,例如
* ListOperations listOperations = redisTemplate.opsForList(); 对应redis中list的操作
* HashOperations hashOperations = redisTemplate.opsForHash(); 对应redis中的hash操作
* redisTemplate.opsForValue() 对应redis中string的操作
* 这里应该了解为什么要先单独熟悉redis的特点了,毕竟方法名都对应了操作
*/
@GetMapping("add/{attr}/{value}")
public void addName(@PathVariable String attr,@PathVariable String value){
redisTemplate.opsForValue().set(attr,value);
System.out.println(attr+" has been add to redis value :"+ value);
}
@GetMapping("get/{attr}")
public String getName(@PathVariable String attr){
String o = (String)redisTemplate.opsForValue().get(attr);
System.out.println("you are to getting "+attr+" and value is "+o);
return o;
}
@GetMapping("del/{attr}")
public String delName(@PathVariable String attr){
Boolean delete = redisTemplate.delete(attr);
System.out.println("you are to remove "+attr);
return String.valueOf(delete);
}
}
编写完以上controller,就可以在浏览器get请求访问测试了,截图如下:
启动springboot程序,浏览器访问
控制台打印:
redis客户端通过keys * 指令查看是否加入了key
可以看到访问url,数据加入到了redis
现在可以想一下如何做个redis缓存,可以利用 spring的aop在想要缓存的方法上进行拦截,查之前先去redis中查,查到直接返回,查之后更新redis
如果方法名不是很规范固定,那就通过自定义注解,通过aop拦截自定的注解进行缓存,这样可以很好的指定哪些需要缓存
上面是我自己的思路,事实上很容易实现,spring也已经给我们提供了实现(有时候对这些实现没什么太大的好感,感觉总是提高学习成本)
spring提供了 @Cacheable注解用来缓存(同样的还有更新,删除缓存等注解,可自行搜索学习,直接搜 @Cacheable就有了)
例子:
新增一个service方法,用来充当数据库:@Service
public class CatchService {
public User getName(String name){
System.out.println("来到了生产的 : "+name);
return new User(name,20);
}
}
接下来在之前的TestController中增加一个方法,如下:
@Autowired
private CatchService catchService;
@GetMapping("catch/{name}")
@Cacheable(value = "userCatch", key = "#name")
//key代表唯一标识的数据项,这里我以User中的name属性作为标识,value可自定义你想表达的标识
public User catchName(@PathVariable String name){
return catchService.getName(name);
}
接下来浏览器访问(此方法我是追加在前面的TestController中的,注意url)
可以看出首次访问,进入了catchservice,并打印添加了 name 为 developer的User,并且key值为自定义 + User的name属性值
接下来第二次访问试试:
第二次访问时看出,浏览器返回了数据,但控制台未打印任何数据.
因为第一次访问已经加入了redis,第二次直接取出返回
这里注意下,redis缓存有超时时间的,所以自己实验时,俩次访问间隔短一些,接下来是redis缓存的一些配置:
@Bean("redisCacheManager")
@Primary //如果有多个实现类,使用@AutoWired 注解时优先赋值有此注解的类
public CacheManager cacheManager(LettuceConnectionFactory lettuceConnectionFactory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
//jackson序列化的配置,熟悉spring的应该知道redis解析httpJson默认的就是jackson
ObjectMapper mapper = new ObjectMapper();
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
jackson2JsonRedisSerializer.setObjectMapper(mapper);
RedisCacheConfiguration config1 = RedisCacheConfiguration.defaultCacheConfig()
.prefixCacheNameWith("purpose header") //所有缓存请求默认都会带的缓存头
//缓存失效时间
.entryTtl(Duration.ofSeconds(30))
//key序列化方式
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
//value序列化方式
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
//不允许缓存null值
.disableCachingNullValues();
//设置一个初始化的缓存空间set集合
Set<String> cacheNames = new HashSet<>();
cacheNames.add("my-redis-cache1");
//对每个缓存空间应用不同的配置
Map<String, RedisCacheConfiguration> configurationMap = new HashMap<>(2);
configurationMap.put("my-redis-cache1", config1);
return RedisCacheManager.builder(lettuceConnectionFactory)
//默认缓存配置
.cacheDefaults(config1)
//初始化缓存空间
.initialCacheNames(cacheNames)
//初始化缓存配置
.withInitialCacheConfigurations(configurationMap).build();
}
上面的配置可直接追加在 之前的配置 bean后面,当然随便放在哪个类,标注了@Configuration就行
上面包含了redisCatch缓存时间,序列化方式,公共头等一些配置(其实一些好多我也没搞懂啥意思,只是从网上粘贴过来的,大致知道怎么用,如果小伙伴有搜到jackson,redis缓存配置详解的学习文档,欢迎留言给我连接,我也学习以下)
另外使用了
- // @Cacheable:触发缓存写入。
- // @CacheEvict:触发缓存清除。
- // @CachePut:更新缓存(不会影响到方法的运行)。
- // @Caching:重新组合要应用于方法的多个缓存操作。
- // @CacheConfig:设置类级别上共享的一些常见缓存设置。
注解的缓存操作,在redis连接不上时会直接抛出异常,但当redis只是作为缓存时,我们不希望缓存失效就报错,而是缓存失效走正常流程
此时只需要重写 CachingConfigurerSupport 中的异常处理方法,编写自己想要处理的逻辑(如打印日志,发送邮件通知开发着,根据反射判断哪些方法就是想抛出异常等),最后用@Bean注入即可
@Bean
public CachingConfigurerSupport cachingConfigurerSupport(){
return new CachingConfigurerSupport(){
@Override
public CacheErrorHandler errorHandler() {
return new CacheErrorHandler(){
@Override
public void handleCacheGetError(RuntimeException e,Cache cache,Object o) {
System.out.println("发生了 获取 异常");
}
@Override
public void handleCachePutError(RuntimeException e,Cache cache,Object o,Object o1) {
System.out.println("发生了 添加 异常");
}
@Override
public void handleCacheEvictError(RuntimeException e,Cache cache,Object o) {
System.out.println("发生了 删除 异常");
}
@Override
public void handleCacheClearError(RuntimeException e,Cache cache) {
System.out.println("发生了 清空 异常");
}
};
}
};
}
以上是关于springboot整合redis的主要内容,如果未能解决你的问题,请参考以下文章