spring缓存支持

spring定义了CacheManagerCache接口用来统一不同的缓存技术。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;