Spring中的缓存技术

418 阅读3分钟

这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战

image.png

可以通过注解或XML的方式配置Spring的缓存,本文仅阐述如何使用注解的方式,笔者认为这是一种更为优雅的方式。

如何配置一个CacheConfig

CacheConfig是用于声明所有缓存的一个统一的配置类,其标志是使用了@EnableCaching注解的Java配置类,如下面的代码所示

@Configurable
@EnableCaching
public class CacheConfig {

    @Bean
    public CacheManager cacheManager() {
        return new ConcurrentMapCacheManager();
    }

}

其本质是创建了一个切面,根据注解和缓存的状态操作缓存中的数据。 @Bean方法用于会返回缓存管理器,Spring内置了五种内存管理器,分别为

  • SimpleCacheManager:需要传入自定义的Collection用于缓存对象的管理。
  • NoOpCacheManager:如果缓存中不存在,仅更新缓存但不会将实际结果返回。
  • ConcurrentMapManager:使用ConcurrentMap实现的缓存。
  • EhCacheCacheManager:基于EhCache实现的本地缓存,直接在JVM层面进行的缓存,速度快效率高
  • CompositeCacheManager:可以配置多个缓存管理器,查找缓存条目时遍历查找

CompositeCacheManager的使用方法如下所示,将ConcurrentMapCacheManager和RedisCacheManager包含在一个CacheManager中。

@Bean
public CacheManager compositeCacheManager(ConcurrentMapCacheManager concurrentMapCacheManager, RedisCacheManager redisCacheManager) {
    CompositeCacheManager compositeCacheManager = new CompositeCacheManager();
    List<CacheManager> cacheManagerList = Lists.newArrayList();
    cacheManagerList.add(concurrentMapCacheManager);
    cacheManagerList.add(redisCacheManager);
    compositeCacheManager.setCacheManagers(cacheManagerList);
    return compositeCacheManager;
}

除了这五种Sping Data还引入了RedisCacheManager和GemfireCacheManager两个缓存管理器,这里笔者简单阐述一下RedisCacheManager的使用方法。

  1. 创建Redis的连接工厂Bean
  2. 创建RedisTemplate
  3. 创建Redis缓存管理器bean
@Configurable
@EnableCaching
public class CacheConfig {

    @Bean
    public JedisConnectionFactory redisConnectionFactory() {
        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
        jedisConnectionFactory.afterPropertiesSet();
        return jedisConnectionFactory;
    }

    @Bean
    public RedisTemplate<String, SourceSystemDto> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, SourceSystemDto> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    @Bean
    public CacheManager redisCacheManager(RedisTemplate<String, SourceSystemDto> redisTemplate) {
        return new RedisCacheManager(redisTemplate);
    }

}

为方法或类添加注解以支持缓存

在Spring中启用缓存会创建一个切面,触发一个或更多的Spring的缓存注解。Spring提供了以下四个注解,均可用于方法和类上,如果应用到类上会作用于所有方法。

  • @Cacheable

    调用方法前先从缓存中取出,如果不存在则调用方法并将方法的返回值放到缓存中,适用于query方法。

  • @CachePut

    调用方法前不会从缓存中取用,但会把返回值放到缓存,适用于save和update操作。

  • @CacheEvict

    从缓存中移除对象。

  • @Caching

    一个分组的注解,用于指定复杂的缓存规则。

@Cacheable@CachePut注解支持value、condition、key和unless四个属性

属性类型描述
valueString[]指定要使用缓存的名称
conditionStringSpEL表达式,仅为true的时候才会应用缓存
keyStringSpEL表达式,用来计算自定义缓存的key
unlessStringSpEL表达式,如果为true则不会将结果放到缓存中

下面的代码演示使用的方法

public class EmployeeService {

    /**
     * 使用employeeCache缓存
     * 使用id查找时使用缓存,将返回值的name和email作为key放入缓存中
     */

    @Caching (
            cacheable = {
                    @Cacheable(value = "employeeCache", key = "#id")
            },
            put = {
                    @CachePut(value = "employeeCacheName", key = "#result.name"),
                    @CachePut(value = "employeeCacheEmail", key = "#result.email")
            }
    )
    public Employee getEmployee(String id) {
        // TODO
    }

    /**
     * 使用employeeCache缓存,自定义缓存为id,当enableCache为true时才会启用缓存
     */
    @CachePut(value = "employeeCache", key = "#result.id", condition = "#employee.enableCache")
    public Employee save(Employee employee) {

    }

    @CacheEvict(value = "employeeCache", key = "#id")
    public void remove(String id) {

    }


}


@Data
class Employee {
    private String id;
    private String name;
    private String email;
    private boolean enableCache;
}

在查询的getEmployee()方法上使用了@Caching注解对多个注解进行了分组,该方法在查询时使用了employeeCache缓存,同时在返回时将id-对象、name-对象、email-对象分别放入不同的缓存中。

在新增/修改的saveEmployee()方法上使用了@CachePut注解,将返回值解析为“id-对象”放入对应的缓存中。

在删除的remove()方法上使用了@CacheEvict注解,在方法调用后将对应的缓存条目从缓存中移除。

@CacheEvict注解额外提供了以下属性用于配置删除的策略

属性类型描述
allEntriesboolean为true时删除缓存中的所有值
beforeInvocationboolean为true时在调用前移除缓存条目,为false则在调用后移除(默认)