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);
    }
}
View Code

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对应的缓存;

posted @ 2020-01-22 19:33  笪笠  阅读(318)  评论(0编辑  收藏  举报