SpringBoot配置Redis缓存
SpringBoot配置Redis缓存
Redis单机缓存
和Ehcache一样,如果在classpath下存在Redis并且Redis已经配置好了,此时默认就会使用
RedisCacheManager作为缓存提供者。Redis 单机使用步骤如下。
1. 创建项目,添加缓存依赖
创建Spring Boot项目,添加spring-boot-starter-cache和Redis依赖,代码如下:
<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>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
2.缓存配置
Redis单机缓存只需要开发者在application.properties中进行Redis配置及缓存配置即可,代码
如下:
#缓存配置
spring.cache.cache-names=c1,c2
spring.cache.redis.time-to-live=1800s
#redis配置
spring.redis.database=0
spring.redis.host=120.55.61.170
spring.redis.port=6379
spring.redis.password=123@456
spring.redis.jedis.pool.max-active=8
spring.redis.jedis.pool.max-idle=8
spring.redis.jedis.pool.max-wait=-1ms
spring.redis.jedis.pool.min-idle=0
代码解释:
- 第2行是配置缓存名称。Redis 中的key都有一个前缀,默认前缀就是“缓存名::”
- 第3行配置缓存有效期,即Redis中key的过期时间。
- 第5~12行是Redis配置,具体含义可以看我的redis整合springboot的博文。
3.开启缓存
接下来在项目入口类中开启缓存,代码如下:
@EnableCaching
@SpringBootApplication
public class TestforeverApplication {
public static void main(String[] args) {
SpringApplication.run(TestforeverApplication.class, args);
}
}
最后创建BookDao和测试的步骤与的第4、5步一致, 这里就不再赘述。
4. 创建BookDao
创建Book实体类和BookService,代码如下:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Book implements Serializable {
private Integer id;
private String name;
private String author;
}
@Repository
@CacheConfig(cacheNames = "book_cache")
public class BookDao {
@Cacheable
public Book getBookById(Integer id) {
System.out.println("getBookById");
Book book = new Book();
book.setId(id);
book.setAuthor("罗贯中");
book.setName("三国演义");
return book;
}
@CachePut(key = "#book.id")
public Book updateBookById(Book book){
System.out.println("updateBookById");
book.setName("三国演义2");
return book;
}
@CacheEvict(key = "#id")
public void deleteBookById(Integer id){
System.out.println("deleteBookById");
}
}
代码解释:
- 在BookDao. 上添加@CacheConfig 注解指明使用的缓存的名字,这个配置可选,若不使用@CacheConfig注解,则直接在@Cacheable注解中指明缓存名字。
- 第4 行在getBookById方法.上添加@Cacheable注解表示对该方法进行缓存,默认情况下,缓存的key是方法的参数,缓存的value是方法的返回值。当开发者在其他类中调用该方法时,首先会根据调用参数查看缓存中是否有相关数据,若有,则直接使用缓存数据,该方法不会执行,否则执行该方法,执行成功后将返回值缓存起来,但若是在当前类中调用该方法,则缓存不会生效。
- @Cacheable注解中还有一个属性condition用来描述缓存的执行时机,例如.@Cacheable(condition = “#id%2==0”)表示当 id对2取模为0时才进行缓存,否则不缓存。
- 如果开发者不想使用默认的key, 也可以像第13行和第19行一样自定义key, 第13行表示缓存的key为参数book对象中id的值,第19行表示缓存的key为参数id。除了这种使用参数定义key的方式之外,Spring 还提供了一个root对象用来生成key,如表所示。
使用root对象生成key
属性名称 | 属性描述 | 用法示例 |
---|---|---|
methodName | 当前方法名 | #root.methodName |
method | 当前方法对象 | #root.method.name |
caches | 当前方法使用的缓存 | #root.cache[0].name |
target | 当前被调用的对象 | #root.target |
targetClass | 当前被调用的对象的Class | #root.targetClass |
args | 当前方法参数数组 | #root.args[0] |
- 如果这些key不能够满足开发需求,开发者也可以自定义缓存key的生成器KeyGenerator,代码如下:
@Component
public class MyKeyGenerator implements KeyGenerator {
@Override
public Object generate(Object target, Method method, Object... params) {
return Arrays.toString(params);
}
}
@Repository
@CacheConfig(cacheNames = "book_cache")
public class BookDao {
@Autowired
MyKeyGenerator myKeyGenerator;
@Cacheable(keyGenerator = "myKeyGenerator")
public Book getBookById(Integer id) {
System.out.println("getBookById");
Book book = new Book();
book.setId(id);
book.setAuthor("罗贯中");
book.setName("三国演义");
return book;
}
}
- 自定义MyKeyGenerator实现KeyGenerator接口,然后实现该接口中的generate方法,该方法的三个参数分别是当前对象、当前请求的方法以及方法的参数,开发者可根据这些信息组成一个新的key 返回,返回值就是缓存的key。 第5行在@Cacheable注解中引用MyKeyGenerator实例即可。
- 第13行@CachePut注解一般用于数据更新方法上,与@Cacheable注解不同,添加了@CachePut注解的方法每次在执行时都不去检查缓存中是否有数据,而是直接执行方法,然后将方法的执行结果缓存起来,如果该key对应的数据已经被缓存起来了,就会覆盖之前的数据,这样可以避免再次加载数据时获取到脏数据。同时,@CachePut 具有和@Cacheable类似的属性,这里不再赘述。
- 第19行@CacheEvict注解一般用于删除方法上,表示移除一个key对应的缓存。@CacheEvict注解有两个特殊的属性: allEntries 和beforeInvocation,其中allEntries 表示是否将所有的缓存数据都移除,默认为false, beforeInvocation 表示是否在方法执行之前移除缓存中的数据,默认为false,即在方法执行之后移除缓存中的数据。
5. 创建测试类
创建测试类,对Service中的方法进行测试,代码如下:
@SpringBootTest
class TestforeverApplicationTests {
@Resource
BookDao bookDao;
@Test
public void contextLoads() {
bookDao.getBookById(1);
bookDao.getBookById(1);
bookDao.deleteBookById(1);
Book bookById = bookDao.getBookById(1);
System.out.println(bookById);
Book book = new Book();
book.setName("张三日记");
book.setAuthor("罗翔");
book.setId(1);
bookDao.updateBookById(book);
Book book1 = bookDao.getBookById(1);
System.out.println(book1);
}
}
执行该方法,控制台打印日志如图所示。
getBookById
deleteBookById
getBookById
Book(id=1, name=三国演义, author=罗贯中)
updateBookById
Book(id=1, name=三国演义2, author=罗翔)
Redis集群缓存
不同于Redis单机缓存,Redis 集群缓存的配置要复杂一些,主要体现在配置上,缓存的使用还是和前面介绍的一样。搭建Redis集群缓存主要分为三个步骤:
- 搭建Redis集群;
- 配置缓存;
- 使用缓存。
下面按照这三个步骤向读者介绍Redis集群缓存的搭建过程。
1. 搭建Redis集群
Redis集群的搭建过程在我的其他博文已经介绍过了,本案例中采用的Redis集群案例和搭建的Redis集群一致,都是8台Redis实例,4主4从,端口从7001到7008,具体搭建过程,这里就不赘述了,读者可以参考6.1.4小节。Redis 集群搭建成功后,通过Spring Data Redis连接Redis集群,这一段配置也和前面的一致,因此这里也不赘述。总之,读者需要参考前面的博文先将Redis集群搭建成功,并且能够在Spring Boot中通过RedisTemplate访问成功。
2. 配置缓存
当Redis集群搭建成功,并且能够从Spring Boot项目中访问Redis 集群后,只需要进行简单的Redis缓存配置即可,代码如下:
@Configuration
public class RedisCacheConfig {
@Autowired
RedisConnectionFactory conFactory;
@Bean
RedisCacheManager redisCacheManager() {
Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.prefixCacheNameWith("sang")
.disableCachingNullValues()
.entryTtl(Duration.ofMillis(30000));
configMap.put("c1", redisCacheConfiguration);
RedisCacheWriter redisCacheWriter = RedisCacheWriter.lockingRedisCacheWriter(conFactory);
RedisCacheManager redisCacheManager=new RedisCacheManager(redisCacheWriter,RedisCacheConfiguration.defaultCacheConfig(),configMap);
return redisCacheManager;
}
}
代码解释:
- 在配置Redis集群时,已经向Spring容器中注册了一个JedisConnectionFactory的实例,这里将之注入到 RedisCacheConfig 配置文件中备用(RedisConnectionFactory 是JedisConnectionFactory的父类)。
- 在RedisCacheConfig中提供RedisCacheManager的实例,该实例的构建需要三个参数,第一个参数是一个cacheWriter, 直接通过lockingRedisCacheWriter 方法构造出来即可;第二个参数是默认的缓存配置;第三个参数是提前定义好的缓存配置。
- RedisCacheManager构造方法中第三个参数是一个提前定义好的缓存参数,它是一个Map类型的参数,该Map中的key就是指缓存名字,value就是该名称的缓存所对应的缓存配置,例如key的前缀、缓存过期时间等,若缓存注解中使用的缓存名称不存在于Map中,则使用RedisCacheManager构造方法中第二个参数所定义的缓存策略进行数据缓存。例如如下两个缓存配置:
@Cacheable(value="c1")
@Cacheable(value="c1")
-
第1行的注解中,c1存在于configMap 集合中,因此使用的缓存策略是configMap集合中c1所对应的缓存策略,c2不存在于configMap集合中,因此使用的缓存策略是默认的缓存策略。
-
本案例中默认缓存策略通过调用RedisCacheConfiguration 中的defaultCacheConfig方法获取,该方法部分源码如下:
public static RedisCacheConfiguration defaultCacheConfig(@Nullable ClassLoader classLoader) {
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
registerDefaultConverters(conversionService);
return new RedisCacheConfiguration(Duration.ZERO, true, true, CacheKeyPrefix.simple(),
SerializationPair.fromSerializer(RedisSerializer.string()),
SerializationPair.fromSerializer(RedisSerializer.java(classLoader)), conversionService);
}
- 由这一段源码可以看到,默认的缓存过期时间为0,即永不过期;第二个参数true表示允许存储null,第三个参数true表示开启key的前缀,第四个参数表示key的默认前缀是“缓存名::”,接下来两个参数表示key和value的序列化方式,最后一个参数则是一个类型转换器。
- 本案例中第8~12行是一个自定义的缓存配置,第10行设置了key的前缀为“sang:", 第11行禁止缓存一个null,第12行设置缓存的过期时间为30分钟。
3.开启缓存
接下来在项目入口类中开启缓存,代码如下:
@EnableCaching
@SpringBootApplication
public class TestforeverApplication {
public static void main(String[] args) {
SpringApplication.run(TestforeverApplication.class, args);
}
}
然后创建一个BookDao使用缓存,代码如下:
@Repository
public class BookDao {
@Cacheable(value = "c1")
public String getBookById(Integer id) {
System.out.println("getBookById");
return "这本书是三国演义";
}
@CachePut(value = "c1")
public String updateBookById(Integer id) {
System.out.println("updateBookById");
return "这是一本全新的三国演义";
}
@CacheEvict(value = "c1")
public void deleteBookById(Integer id) {
System.out.println("deleteBookById");
}
@Cacheable(value = "c2")
public String getBookById2(Integer id) {
System.out.println("getBookById");
return "这本书是红楼梦";
}
}
最后创建单元测试,代码如下:
@SpringBootTest
class TestforeverApplicationTests {
@Resource
BookDao bookDao;
@Test
public void contextLoads() {
bookDao.getBookById(100);
String book = bookDao.getBookById(100);
System.out.println(book);
bookDao.updateBookById(100);
String book2 = bookDao.getBookById(100);
System.out.println(book2);
bookDao.deleteBookById(100);
bookDao.getBookById(100);
bookDao.getBookById2(88);
}
}
单元检测结果如下
getBookById
这本书是三国演义
updateBookById
这是一本全新的三国演义
deleteBookById
getBookById
由单元测试可以看到,一开始做了两次查询,但是查询方法只调用了一-次,因为第二次使用了缓存;接下来执行了更新,当更新成功后再去查询,此时缓存也已更新成功;接下来执行了删除,删除成功后再去执行查询,查询方法又被调用,说明缓存也已经被删除了;最后查询了一个id为99的记录,这次使用的是默认缓存配置。在Redis服务器上也可以看到缓存结果,如下所示。
120.55.61.170:7002> ttl sangc1::100
(integer) 28
120.55.61.170:7002> get sangc1::100
"\xac\xed\x00\x05t\x00\x18\xe8\xbf\x99\xe6\x9c\xac\xe4\xb9\xa6\xe6\x98\xaf\xe4\xb8\x89\xe5\x9b\xbd\xe6\xbc\x94\xe4\xb9\x89"
127.0.0.1:7001> get c2::99
"\xac\xed\x00\x05t\x00\x15\xe8\xbf\x99\xe6\x9c\xac\xe4\xb9\xa6\xe6\x98\xaf\xe7\xba\xa2\xe6\xa5\xbc\xe6\xa2\xa6"
127.0.0.1:7001> ttl c2::99
(integer) -1
id为100的记录使用的缓存名为c1,因此key的前缀是“sangc1:",这是上文配置的,过期时间还剩28秒(. 上文配置的过期时间是30秒) ;而id为99的记录使用的缓存名称为c2,因此使用了默认的缓存配置,默认的前缀为“缓存名:”,即“c2::”, 默认的过期时间是永不过期。
更多推荐
所有评论(0)