Spring in action读书笔记(十一)Spring启用缓存
一、Spring启用对缓存的支持
配置CacheConfig类
@Configuration @EnableCaching public class CachingConfig { }
二、配置缓存管理器
缓存管理器有多种实现,如
SimpleCacheManager
NoOpCacheManager
ConcurrentMapCacheManager
CompositeCacheManager
EhCacheCacheManager
RedisCacheManager
这些类是CacheManager接口的具体实现,CacheManager接口定义如下:
public interface CacheManager { /** * Get the cache associated with the given name. * <p>Note that the cache may be lazily created at runtime if the * native provider supports it. * @param name the cache identifier (must not be {@code null}) * @return the associated cache, or {@code null} if such a cache * does not exist or could be not created */ @Nullable Cache getCache(String name); /** * Get a collection of the cache names known by this manager. * @return the names of all caches known by the cache manager */ Collection<String> getCacheNames(); }
主要定义了缓存名称集合类获取方法及根据缓存名称获取具体缓存操作类的方法(Cache接口的实现)。
本文中主要使用了EhCacheCacheManager、RedisCacheManager以及CompositeCacheManager
这里给出对象类Category.java
Category类
package test; import java.util.Objects; import java.util.Random; public class Category { private int id; private String name; //随机数,用来标识是不是同一个对象 private int random; public Category(int id, String name) { this.id = id; this.name = name; random = new Random().nextInt(); } public Category() { random = new Random().nextInt(); } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Category{" + "id=" + id + ", name='" + name + '\'' + ", random=" + random + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Category category = (Category) o; return id == category.id && Objects.equals(name, category.name); } @Override public int hashCode() { return Objects.hash(id, name); } }
1、配置EhCacheCacheManager
@Bean public EhCacheCacheManager ehCacheCacheManager(net.sf.ehcache.CacheManager cm) { return new EhCacheCacheManager(cm); } @Bean public EhCacheManagerFactoryBean ehCacheManagerFactoryBean() { EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean(); ehCacheManagerFactoryBean.setConfigLocation(new ClassPathResource("ehcache.xml")); return ehCacheManagerFactoryBean; }
在src/java/resources目录下新建ehcache.xml文件, 配置了一个名为test-cache的缓存,最大的堆存储为50MB, 存货时间为100秒。
(EhCache相关参数参见http://www.ehcache.org/documentation/configuration)
<ehcache> <cache name="test-cache" maxBytesLocalHeap="50m" timeToLiveSeconds="100"> </cache> </ehcache>
2、配置RedisCacheManager
配置了一个名为test-cache的缓存, key使用StringRedisSerializer序列化,value使用Jackson2JsonRedisSerializer进行序列化
@Bean public RedisCacheManager redisCacheManager(RedisConnectionFactory cf) { RedisCacheManager.RedisCacheManagerBuilder cacheManagerBuilder = RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(cf); Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>(); RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer(Object.class))) .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())); cacheConfigurations.put("test-cache", cacheConfiguration); cacheManagerBuilder.withInitialCacheConfigurations(cacheConfigurations); return cacheManagerBuilder.build(); } @Bean public <T> Jackson2JsonRedisSerializer<T> jackson2JsonRedisSerializer(Class<T> clazz) { Jackson2JsonRedisSerializer<T> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(clazz); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); return jackson2JsonRedisSerializer; }
3、使用CompositeCacheManager使用多个缓存管理器
CompositeCacheManager通过一个或多个缓存管理器来进行配置,在查找缓存时会迭代缓存管理器,查找之前缓存的值。
注意:CompositeCacheManager仅仅根据是否存在对应的缓存名称来获取对应的缓存操作类的,而不是遍历所有缓存管理器来查找是否存在缓存值。具体实现如下:
public Cache getCache(String name) { for (CacheManager cacheManager : this.cacheManagers) { Cache cache = cacheManager.getCache(name); if (cache != null) { return cache; } } return null; }
CompositeCacheManager配置如下:
@Bean
@Primary
public CompositeCacheManager compositeCacheManager(net.sf.ehcache.CacheManager cm, RedisConnectionFactory cf) {
CompositeCacheManager compositeCacheManager = new CompositeCacheManager();
List<CacheManager> managers = new ArrayList<>();
managers.add(redisCacheManager(cf));
managers.add(new EhCacheCacheManager(cm));
compositeCacheManager.setCacheManagers(managers);
return compositeCacheManager;
}
如上配置中,假如RedisCacheManager和EhCacheCacheManager定义了相同名称的缓存,如test-cache, 则EnCacheCacheManager中的test-cache缓存会被屏蔽。
三、为方法添加注解以支持缓存
Spring提供了四个注解
@Cacheable : 方法调用之前会查找方法的返回值,如果存在,则返回缓存的值,否则调用方法并将返回值放到缓存中。
@CachePut: 在方法调用前不会检查缓存,方法始终都会被调用;
@CacheEvict: 清除缓存
@Caching: 同时应用多个其他的缓存注解;
1、增加缓存示例如下:
@Component public class CategoryServiceImpl { @Cacheable(value = "test-cache", key="{'category' + #root.args[0]}", condition = "#id == 1", cacheManager = "ehCacheCacheManager") public Category get(int id) { Category category = new Category(id, "name" + id); System.out.println("创建" + category); return category; } @CacheEvict(value = "test-cache", key="{'category'+#root.args[0]}") public void remove(int id) { } }
get方法中创建了Category对象,如果使用@Cacheable注解,无论调用多少次,Category对象之会被调用一次。
junit测试:
测试输出结果: @Autowired private CategoryServiceImpl categoryService; @Test public void test() { System.out.println(categoryService.get(1)); System.out.println(categoryService.get(1)); }
创建Category{id=1, name='name1', random=1977760562}
Category{id=1, name='name1', random=1977760562}
Category{id=1, name='name1', random=1977760562}
CategoryServiceImpl类get方法中@Cacheable注解的cacheManager可以换成redisCacheManager,这样就可以向Redis中写入数据,Redis中如果已经存在,则直接返回缓存值。
2、缓存注解的属性:
@Cacheable和@CachePut注解的的方法必须有返回值,共有属性如下:
属性 | 类型 | 描述 |
value | String[] | 使用的缓存名称 |
condition | String | SpEL表达式,如果值为false,缓存不会启用 |
key | String | SpEL表达式,用来计算自定义的缓存key |
unless | String | SpEL表达式,如果值为true,返回值不会放到缓存中 |
condition实在方法调用前计算,可以阻止方法的运行,而unless属性是在方法调用后执行的。
自定义缓存key:
Spring提供了多个用来定义缓存规则的SPEL拓展
表达式 | 描述 |
#root.args | 传递给缓存方法的参数,形式为数组 |
#root.caches | 该方法执行时所对应的缓存,形式为数组 |
#root.target | 目标对象 |
#root.targetClass | 目标对象的类,是#root.target.class的简写形式 |
#root.method | 缓存方法 |
#root.methodName | 缓存方法的名字,是#root.method.name的简写 |
#result | 方法调用的返回值(不能用在@Cacheable注解上 |
#Argument | 任意的方法参数名(如#argName)或参数索引(如#a0或#p0) |
CategoryServiceImpl类中get方法指定缓存key为 "category1+id"
移除缓存:
@CacheEvict不会像缓存中增加任何东西,可以注解在返回值为void的方法上,同样可以指定移除哪一个cacheManage对应的缓存;