Spring Cache
Posted lshare
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Cache相关的知识,希望对你有一定的参考价值。
为什么要使用 Spring Cache 管理缓存?
让 Spring 来管理 Bean 的缓存具有以下优势:
- Spring 支持 HashMap 缓存,Redis 缓存以及自定义的缓存方式;
- Spring 缓存几乎不需要写代码,只需要配置好并声明好注解。
快速开始
(1)依赖引入
这里使用 Spring 的依赖管理器来管理 Spring Cache 的版本,会自动处理内部的模块间依赖,这也是推荐的方式。
buildscript
repositories
mavenCentral()
dependencies
classpath("org.springframework.boot:spring-boot-gradle-plugin:2.0.5.RELEASE")
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
repositories
mavenCentral()
dependencies
compile("org.springframework.boot:spring-boot-starter-cache")
(2)启用缓存
在 SpringApplication 配置类的地方添加以下注解以启用缓存功能。
@SpringBootApplication
@EnableCaching
(3)ConcurrentHashMap 缓存
当没有配置其他缓存库时,默认使用 ConcurrentHashMap
作为缓存仓库。
(3.1)一个简单的实体类
public class Customer
public String id;
public String firstName;
public String lastName;
public Customer()
public Customer(String firstName, String lastName)
this.firstName = firstName;
this.lastName = lastName;
(3.2)一个 Repository
public class CustomerRepository
@Cachable
Customer getByFirstName(String firstName)
// 这里应该是从数据库查询数据,DEMO 简省成直接新建了。
return new Customer(firstName, "Jobs");
(3.3)测试一下
如果缓存成功了,那么以下代码执行结果的 HashCode 是一致的。
@Component
public class AppRunner implements CommandLineRunner
private static final Logger logger = LoggerFactory.getLogger(AppRunner.class);
private final CustomerRepository customerRepository;
public AppRunner(CustomerRepository customerRepository)
this.customerRepository = customerRepository;
@Override
public void run(String... args) throws Exception
logger.info("John -->" + customerRepository.getByFirstName("John").hashCode());
logger.info("John -->" + customerRepository.getByFirstName("John").hashCode());
logger.info("John -->" + customerRepository.getByFirstName("John").hashCode());
(4)配合 Redis 缓存
(4.1)添加 Redis 依赖
在前面的依赖之下再额外新增 Redis 相关的依赖,如下:
// 本环境中的 spring-data-redis 为 1.8.7.RELEASE 版本
// 高版本的配置略有不同,请留意
compile ("org.springframework.data:spring-data-redis")
compile "redis.clients:jedis:2.9.0"
(4.2)配置 Redis
@Configuration
public class RedisConfig
private final static Logger LOGGER = LoggerFactory.getLogger(RedisConfig.class);
private final static Map<String, Long> CACHE_EXPIRE_MAP = new HashMap<>();
static
CACHE_EXPIRE_MAP.put("cache1", 5 * 60L); //second
@Bean
RedisConnectionFactory redisConnectionFactory()
JedisConnectionFactory jedisConFactory = new JedisConnectionFactory();
jedisConFactory.setHostName("localhost");
jedisConFactory.setPort(6379);
return jedisConFactory;
@Bean
StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
return new StringRedisTemplate(redisConnectionFactory);
@Bean
RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory)
RedisTemplate template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
template.afterPropertiesSet();
// 默认为 JdkSerializationRedisSerializer, 配合 @Cacheable 时 KEY 会有序列化值在中间
// 使用 StringRedisSerializer 则不会如此
template.setKeySerializer(new StringRedisSerializer());
return template;
@Bean
public RedisCacheManager cacheManager(RedisTemplate redisTemplate)
RedisCacheManager cm = new RedisCacheManager(redisTemplate);
cm.setCacheNames(CACHE_EXPIRE_MAP.keySet());
cm.setExpires(CACHE_EXPIRE_MAP);
cm.setUsePrefix(true);
cm.setCachePrefix(cacheName -> (cacheName + ":").getBytes());
return cm;
(4.2)序列化实体类
Spring 在将实体类缓存到 Redis 中时进行了序列化操作,如果不对实体类进行序列化将会报错。
public class Customer implements Serializable
public String id;
public String firstName;
public String lastName;
public Customer()
public Customer(String firstName, String lastName)
this.firstName = firstName;
this.lastName = lastName;
(4.3)在需要缓存的位置使用注解,并指定缓存名
如果在使用 Redis 缓存时,没有指定缓存名,将会报错:no cache could be resolved for at least one cache should be provided per cache operation
。
public class CustomerRepository
@Cachable("cache1")
Customer getByFirstName(String firstName)
// 这里应该是从数据库查询数据,DEMO 简省成直接新建了。
return new Customer(firstName, "Jobs");
(4.4)测试一下
测试代码同(3.3)。除此之外还可以通过 Redis CLI 检验缓存结果:
> KEYS *
1) "cache1:cb5775e6-1b39-4f63-85c8-13f134a54f32"
> GET "cache1:cb5775e6-1b39-4f63-85c8-13f134a54f32"
> TTL "cache1:cb5775e6-1b39-4f63-85c8-13f134a54f32"
更进一步
创建自定义的 KeyGenerator
- 使上述的
RedisConfig
继承CachingConfigurerSupport
,这一步很重要,否则创建自定义的KeyGenerator
失败; - 使用
@Bean
声明自定义的KeyGenerator
。代码如下:
@Configuration
public class RedisConfig extends CachingConfigurerSupport
@Bean
@Override
public KeyGenerator keyGenerator()
return new SimpleKeyGenerator()
@Override
public Object generate(Object target, Method method, Object... params)
// 这里使用 [`] 分割参数,更进一步的还可以加入 method 名,或者直接重写一个 KeyGenerator。
return super.generate(target, method, StringUtils.arrayToDelimitedString(params, "`"));
;
这样,就可以覆盖 Spring Cache 默认的 SimpleKeyGenerator
了。
参考
- Caching Data with Spring - spring.io
- Caching - spring.io
- A Guide To Caching in Spring - baeldung.com
- Spring Data Redis
- spring使用redis做缓存 - cnblogs.com
- Spring Cache – Creating a Custom KeyGenerator
以上是关于Spring Cache的主要内容,如果未能解决你的问题,请参考以下文章
你了解Spring从Spring3到Spring5的变迁吗?
Spring全家桶笔记:Spring+Spring Boot+Spring Cloud+Spring MVC
学习笔记——Spring简介;Spring搭建步骤;Spring的特性;Spring中getBean三种方式;Spring中的标签