SpringBoot集成Redis及SpringCache缓存管理
1.集成Redis
1.导入依赖
<!--spirngboot springdata对redis支持-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.配置信息
#数据源配置
spring:
redis:
database: 0
host: 127.0.0.1
port: 6379
password: 123456
jedis:
pool:
max-wait: 2000ms
max-active: 8
3.注入redisTemplate使用
@RunWith(SpringRunner.class) //使用springboot2.6.13的版本可以省略这个注解
@SpringBootTest(classes = App.class) //如果测试类和启动不在同一个包下classes必须要写
public class SpringbootDataRedisApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
public void test() throws Exception{
redisTemplate.opsForValue().set("name","zs");
System.out.println(redisTemplate.opsForValue().get("name"));
}
}
4.自定义配置redis中key和value的序列化
@Configuration
public class RedisConfig {
// 注入Redis连接工厂
@Resource
private RedisConnectionFactory factory;
/**
* @Description: 自定义RedisTemplate对象注入Bean容器中
* @Author: Neuronet
* @Date: 2023/10/22 18:49
**/
@Bean
public RedisTemplate<Object, Object> redisTemplate() {
// 1.创建一个RedisTemplate对象
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
// 2.指定Redis连接工厂
redisTemplate.setConnectionFactory(factory);
// 3.创建一个JSON格式序列化对象,此处使用的是Redis自己的的序列化器
GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer();
// 4.指定除开hash数据类型之外的数据key和value使用什么序列化器
redisTemplate.setKeySerializer(serializer);
redisTemplate.setValueSerializer(serializer);
// 5.指定hash数据类型的key和value序列化器
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(serializer);
return redisTemplate;
}
}
2.SpringCahe缓存管理
1. SpringCache概述
Spring 3.1 引入了激动人心的基于注解(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(比如EHCache 或者 OSCache),而是一个对缓存使用的抽象,通过在既有代码中加入少量它定义的各种 annotation,即能够达到缓存方法的返回对象的效果
Spring 的缓存技术还具备相当的灵活性。不仅能够使用 SpEL(Spring Expression Language)来定义缓存的 key 和各种 condition,还提供开箱即用的缓存暂时存储方案,也支持和主流的专业缓存比如 EHCache 集成
说人话:SpringCahce对缓存流程进行了简化封装,提供了一些注解,我们通过简单的打注解就能实现缓存的添加,修改,删除等,注解如下:
- @Cacheable:触发缓存写入
- @CacheEvict:触发缓存清除
- @CachePut:更新缓存(不会影响到方法的运行)
- @Caching:重新组合要应用于方法的多个缓存操作
- @CacheConfig:设置类级别上共享的一些常见缓存设置
2.SpringCache大致原理
- 1.Spring Cache利用了AOP,实现了基于注解的缓存功能,并且进行了合理的抽象,业务代码不用关心底层是使用了什么缓存框架,只需要简单地加一个注解,就能实现缓存功能了,做到了对代码侵入性做小。
- 2.由于市面上的缓存工具实在太多,SpringCache框架还提供了CacheManager接口,可以实现降低对各种缓存框架的耦合。它不是具体的缓存实现,它只提供一整套的接口和代码规范、配置、注解等,用于整合各种缓存方案,比如Caffeine、Guava Cache、Ehcache。
3.SpringCache注解
3.1. @Cacheable:写缓存
- 方式一:@Cacheable可以用来进行缓存的写入,先也会根据缓存名和key去查询,
如果没有查询到,自动将方法的返回结果存储在缓存中,以便于在后续调用的时候可以直接返回缓存中的值,而不必再执行实际的方法
@Cacheable(cacheNames=“books”,key=”’book1’”) // books::book1
public Book findBook(ISBN isbn) {.
//查询数据库
return 数据;
}
- 方式二:一个方法可以对应多个缓存名称
@Cacheable(cacheNames={“books”, “isbns”},key=”’book1’”)
public Book findBook(ISBN isbn) {..…}
- 方式三@Cacheable的缓存名称是可以配置动态参数的,比如选择传入的参数,如下: (以下示例是使用SpEL声明,如果您不熟悉SpEL,可以阅读Spring Expression Language)
@Cacheable(cacheNames=“books”, key=“#isbn”) //参数值作为Key
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
@Cacheable(cacheNames=“books”, key=“#isbn.rawNumber”)
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
- 方式四
@Cacheable还可以设置根据条件判断是否需要缓存
condition:取决于给定的参数是否满足条件
unless:取决于返回值是否满足条件(除非)
以下是一个简单的例子:key有默认的名字
@Cacheable(cacheNames=“book”, condition=“#name.length() < 32”)
public Book findBook(String name)
@Cacheable(cacheNames=“book”,condition=“#name.length()<32”, unless=“#result.hardback”)
public Book findBook(String name)
3.2. @CachePut:修改缓存
@CachePut:当需要更新缓存而不干扰方法的运行时 ,可以使用该注解。也就是说,始终执行该方法,并将结果放入缓存
(已经有缓存就更新缓存),注解参数与@Cacheable相同。 以下是一个简单的例子:
@CachePut(cacheNames="book", key="#isbn")
public Book updateBook(ISBN isbn, BookDescriptor descriptor) {
}
通常强烈建议不要对同一方法同时使用@CachePut和@Cacheable注解,因为它们具有不同的行为。可能会产生不可思议的BUG哦
3.3. @CacheEvict:删除缓存
@CacheEvict:删除缓存的注解,这对删除旧的数据和无用的数据是非常有用的。这里还多了一个参数(allEntries),设置allEntries=true时,可以对整个条目进行批量删除。 以下是个简单的例子:
@CacheEvict(cacheNames="books", key="'book1'")
public void loadBooks(InputStream batch)
//对cacheNames进行批量删除
@CacheEvict(cacheNames="books", allEntries=true)
public void loadBooks(InputStream batch)
3.4. @Caching:组合注解
@Caching在使用缓存的时候,有可能会同时进行更新和删除,会出现同时使用多个注解的情况。而@Caching可以实现。 以下是个简单的例子:
@Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames="secondary", key="#p0") })
public Book importBooks(String deposit, Date date)
3.5. @CacheConfig: 统一配置
@CacheConfig:缓存提供了许多的注解选项,但是有一些公用的操作,我们可以使用@CacheConfig在类上进行全局设置。 以下是个简单的例子:
@CacheConfig(cacheName="books")
public class BookRepositoryImpl implements BookRepository {
//注意:如果没有指定key的话,keyGenerator会自动生成key导致删除缓存失败,所以下面的缓存操作需要指定key
@Cacheable(key="'book1'")
public Book findBook(ISBN isbn) {...}
}
4.SpringCache实战
1.导入依赖
<!-- SpringCache依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
2.配置SpringCache集成redis
/**
* @Description: SpringCache配置
* @Author: Neuronet
* @Date: 2023/10/22 19:32
**/
@Configuration
public class CacheConfig extends CachingConfigurerSupport {
@Resource
private RedisConnectionFactory factory;
@Bean
@Override
public CacheResolver cacheResolver() {
return new SimpleCacheResolver(cacheManager());
}
@Bean
@Override
public CacheErrorHandler errorHandler() {
// 用于捕获从Cache中进行CRUD时的异常的回调处理器。
return new SimpleCacheErrorHandler();
}
//缓存管理器
@Bean
@Override
public CacheManager cacheManager() {
RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.disableCachingNullValues() //不允许空值
.serializeValuesWith(RedisSerializationContext.SerializationPair
//值使用JSON序列化
.fromSerializer(new GenericJackson2JsonRedisSerializer()));
return RedisCacheManager.builder(factory).cacheDefaults(cacheConfiguration).build();
}
}
3.开启springCache
在启动类注解:@EnableCaching
4.添加缓存
缓存注解不能加在内部方法上,比如:方法A调用方法B,给方法B加上缓存注解会失效,因为内部方法调用代理会失效,在A方法上打注解即可
//会先根据articleType::typeTree从redis获取,如果有直接用缓存的。如果没有就执行方法体的代码查询数据库
@Cacheable(cacheNames = "articleType", key = "'typeTree'")
public List<ArticleType> getTypeTree() {
Object obj = redisTemplate.opsForValue().get("typeTree");
if(obj == null){//redis没有
List<ArticleType> typeTree = typeTree();
redisTemplate.opsForValue().set("typeTree",typeTree);
return typeTree;
}
return typeTree();
}