spring缓存支持
spring定义了CacheManager
和Cache
接口用来统一不同的缓存技术。CacheManager是Spring提供的各种缓存技术抽象接口,Cache接口包含缓存的各种操作。
针对不同的缓存技术需要实现不同的CacheManager
CacheManager | 描述 |
EhCacheCacheManager | 使用EhCache作为缓存技术 |
GuavaCacheManager | 使用Google的GuavaCache作为缓存技术 |
RedisCacheManager | 使用Redis作为缓存技术 |
每种缓存技术都有很多的额外配置,但配置cacheManager是必不可少的。
声明式缓存注解
spring提供了4个注解来声明缓存规则
注解 | 解释 |
@Cacheable | 在方法执行前spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据,调用方法并将方法返回值放到缓存 |
@CachePut | 无论怎样都会将方法的返回值放到缓存中 |
@CacheEvict | 将一条或多条数据从缓存中删除 |
@Caching | 可以该注解可以自定义组合多个注解 |
@Cacheable、@CachePut、@CacheEvict都有value属性,指定的是要使用的缓存名称;key属性指定的是数据在缓存中的key;
开启声明式缓存支持
@Configuration
@EnableCaching
public class AppConfig{}
以上缓存规则应用举例
springboot环境下,使用缓存技术只需在项目中导入相关缓存技术的依赖包,并在配置类中使用@EnableCaching开启缓存支持即可。
1、@Cacheable
的作用 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存。
当调用这个方法的时候,会先从一个value为 accountCache 的缓存中查询,如果没有再执行目标方法(即查询数据库),并将执行的结果存入缓存中,否则返回缓存中的对象。
@Cacheable(value="accountCache")// 使用了一个缓存命名空间 accountCache,key为userName
public Account getAccountByName(String userName) {
// 方法内部实现不考虑缓存逻辑,直接实现业务
System.out.println("real query account."+userName);
return getFromDB(userName);
}
2、@CacheEvict
的作用 主要针对方法配置,能够根据一定的条件对缓存进行清空。可以使用在类或方法上。
// 根据account.name清空accountCache 缓存
@CacheEvict(value="accountCache",key="#account.getName()")
public void updateAccount(Account account) {
updateDB(account);
}
// 清空accountCache 缓存
@CacheEvict(value="accountCache",allEntries=true)
public void reload() {
reloadAll()
}
// 根据条件清空缓存 accountCache
@Cacheable(value="accountCache",condition="#userName.length() <=4")
public Account getAccountByName(String userName) {
// 方法内部实现不考虑缓存逻辑,直接实现业务
return getFromDB(userName);
}
3、@CacheConfig
每个@Cacheable(value=“accountCache”)都要写一遍,很麻烦, 使用@CacheConfig这个配置声明一次就够了。
@CacheConfig("books")
public class BookRepositoryImpl implements BookRepository {
@Cacheable
public Book findBook(ISBN isbn) {...}
}
4、@Caching
组合多个注解
@Caching(put = {
@CachePut(value = "user", key = "#user.id"),
@CachePut(value = "user", key = "#user.username"),
@CachePut(value = "user", key = "#user.email")
})
public User save(User user) {}
5、上面的写法不易理解,可以使用自定义注解
@Caching(put = {
@CachePut(value = "user", key = "#user.id"),
@CachePut(value = "user", key = "#user.username"),
@CachePut(value = "user", key = "#user.email")
})
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface UserSaveCache {
}
//下面这样引用显得代码很干净
@UserSaveCache
public User save(User user){}
6、@CachePut
缓存新增的或更新的数据到缓存,其中缓存名称为accountCache,数据的key是account.name
@CachePut(value="accountCache",key="#account.getName()")
public Account updateAccount(Account account) {
return updateDB(account);
}
7、如何设置缓存有效期
// 对基于注解的Redis缓存数据统一设置有效期为1分钟,单位毫秒
spring.cache.redis.time-to-live=60000
切换缓存技术
切换缓存技术主要是引入相关依赖或者配置。
1、EhCache
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.6</version>
</dependency>
Ehcache所需的配置文件ehcache.xml只需放在类路径下,springboot会自动扫描
springboot会自动给我们配置EhCacheCacheManager的Bean
2、Guava
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>27.0-jre</version>
</dependency>
springboot会自动给我们配置GuavaCacheManager的Bean
3、Redis
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
springboot会自动配置RedisCacheManager以及RedisTemplate的Bean
StringRedisTemplate应用
spring封装了RedisTemplate来操作redis,springboot默认支持StringRedistTemplate,他继承了RedisTemplate,可以直接引入,适合key-value都是字符串的情况。两者的数据序列化策略不同,前者是string,后者为jdk的序列化
- opsFor
redisTemplate.opsForValue().set("name","zhangsan")
redisTemplate.opsForValue().get("name")
- opsForList
redisTemplate.opsForList().leftpush("username","zhangsan")
redisTemplate.opsForList().leftpush("username","lisi")
opsForList还有leftPush、leftPop、rightPush、rightPop等操作
- opsForHash
redisTemplate.opsForHash().put("cache","username","zhangsan")
String username = (String)redisTemplate.opsForHash().get("cache","username")
- 其他还有opsForSet、opsForZSet操作集合
对redis集群的支持
springboot对redis集群的支持不是很友好,可以通过bean的方式自定义注入
@Data
@ConfigurationProperties(prefix = "redis.cache")
public class RedisProperties {
private int expireTime;
private String clusterNames;
private int commandTime;
}
@Configuration
public class RedisClusterConfig {
@Autowired
private RedisProperties redisProperties;
@Bean
public JedisCluster getJedisCluster() {
List<String> list = Arrays.asList(redisProperties.getClusterNames().split(","));
HashSet<HostAndPort> nodes = new HashSet<>();
list.stream().forEach(node -> {
String[] ipPortPair = node.split(":");
nodes.add(new HostAndPort(ipPortPair[0].trim(), Integer.valueOf(ipPortPair[1].trim())));
});
return new JedisCluster(nodes, redisProperties.getCommandTime());
}
}
然后在service实现类里引入即可。
@Autowire
private JedisCluster jedisCluster;