Spring Cache抽象 自3.1版以来,Spring 提供了对现有Spring应用程序透明地添加缓存的支持。下面介绍几个常用的注解。
@Cacheable cacheNames 1 2 @Cacheable ("books" )public Book findBook (ISBN isbn) {...}
大多数情况下,只声明一个缓存,但注释允许指定多个名称,以便使用多个缓存。
1 2 @Cacheable ({"books" , "isbns" })public Book findBook (ISBN isbn) {...}
Custom Key Generation Declaration @Cacheable
注解允许用户指定通过其关键属性生成key,可使用SpEL表达式来选择参数(或它们的嵌套属性)。
Spring Cache提供了一些供我们使用的SpEL上下文数据,通过#来引用,具体可查看Spring官网:http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#cache-spel-context。
1 2 3 4 5 6 7 8 @Cacheable (cacheNames="books" , key="#isbn" )public Book findBook (ISBN isbn, boolean checkWarehouse, boolean includeUsed) @Cacheable (cacheNames="books" , key="#isbn.rawNumber" ) public Book findBook (ISBN isbn, boolean checkWarehouse, boolean includeUsed) @Cacheable (cacheNames="books" , key="T(someType).hash(#isbn)" ) public Book findBook (ISBN isbn, boolean checkWarehouse, boolean includeUsed)
还可以定义一个自定义的keyGenerator来生成key
1 2 @Cacheable (cacheNames="books" , keyGenerator="myKeyGenerator" )public Book findBook (ISBN isbn, boolean checkWarehouse, boolean includeUsed)
key和keyGenerator参数是互斥的,指定两者的操作将导致异常。
Synchronized caching 1 2 @Cacheable (cacheNames="foos" , sync=true )public Foo executeExpensiveOperation (String id) {...}
这是一个可选功能,默认为false,有的缓存库可能不支持它。一般框架提供的所有CacheManager实现都支持它。
Conditional caching condition: 在执行方法前,condition的值为true,则缓存数据 unless :在执行方法后,判断unless ,如果值为true,则不缓存数据 conditon和unless可以同时使用,则此时只缓存同时满足两者的记录 @Cacheable
支持通过condition判断是否进行缓存,该条件参数采用SpEL
1 2 @Cacheable (cacheNames="book" , condition="#name.length() < 32" )public Book findBook (String name)
此外还可以使用unless,与condition不同,unless根据返回结果 来判断是否缓存。
注意unless表示如果不,判断条件是相反的。例如,我们只想缓存平装书,不缓存精装书:
1 2 @Cacheable (cacheNames="book" , condition="#name.length() < 32" , unless="#result.hardback" )public Book findBook (String name)
支持java.util.Optional,仅在其存在时将其内容用作缓存值。注意,#result始终引用业务实体Book而不是Optional。
1 2 @Cacheable (cacheNames="book" , condition="#name.length() < 32" , unless="#result?.hardback" )public Optional<Book> findBook (String name)
@CachePut 需要更新缓存而不干扰方法执行的情况,可以使用@CachePut
注释,它支持与@Cacheable
相同的参数。
如果使用 @Cacheable
注释,则当重复使用相同参数调用方法的时候,方法本身不会被调用执行,即方法本身被略过了,取而代之的是方法的结果直接从缓存中找到并返回了。
现实中并不总是如此,有些情况下我们希望方法一定会被调用,因为其除了返回一个结果,还做了其他事情,例如记录日志,调用接口等,这个时候,我们可以用 @CachePut
注释,这个注释可以确保方法被执行,同时方法的返回值也被记录到缓存中。
1 2 @CachePut(cacheNames="book", key="#isbn") public Book updateBook(ISBN isbn, BookDescriptor descriptor)
注意,对同一方法使用@CachePut和@Cacheable注释是不鼓励的,因为它们具有不同的行为。
@CacheEvict 注解@CacheEvict
定义了执行缓存删除的方法,它参数与@Cacheable
类似,此外具有一个额外的参数allEntries,它表示是否需要删除命名空间下所有的缓存,而不仅仅是基于key的一条,默认为false :
1 2 @CacheEvict (cacheNames="books" , allEntries=true )public void loadBooks (InputStream batch)
@Caching @Caching
允许在同一个方法上使用多个嵌套的@Cacheable
,@CachePut
和@CacheEvict
:
1 2 3 4 5 6 7 8 9 10 11 @Caching (evict = { @CacheEvict ("primary" ), @CacheEvict (cacheNames="secondary" , key="#p0" ) })public Book importBooks (String deposit, Date date) @Caching ( put = { @CachePut(value = "user" , key = "#user.id" ) , @CachePut (value = "user" , key = "#user.name" ) , @CachePut (value = "user" , key = "#user.account" ) } ) public User updateUser (User user)
@CacheConfig @CacheConfig
类级别的注解:如果我们在此注解中定义cacheNames,则此类中的所有方法上 @Cacheable
的cacheNames默认都是此值。当然@Cacheable
也可以重定义cacheNames的值
1 2 3 4 5 6 @CacheConfig ("books" )public class BookRepositoryImpl implements BookRepository { @Cacheable public Book findBook (ISBN isbn) {...} }
Spring Boot集成Redis做缓存 Redis配置 配置application.properties,列出的均是redis配置的默认值
1 2 3 4 5 6 7 8 #REDIS (RedisProperties) spring.redis.host= spring.redis.password= spring.redis.port=6379 spring.redis.pool.max-idle=8 spring.redis.pool.min-idle=0 spring.redis.pool.max-active=100 spring.redis.pool.max-wait=-1
Spring Boot会执行RedisAutoConfiguration,自动初始化初始化RedisTemplate和StringRedisTemplate
当在配置类(@Configuration
)上使用@EnableCaching
注解时,Spring Boot就会自动配置缓存基础架构。
1 2 3 4 @Configuration @EnableCaching public class AppConfig {}
如果Redis可用且已配置,Spring Boot会自动配置RedisCacheManager。
Spring Boot初始化RedisCacheManager的过程 缓存管理接口org.springframework.cache.CacheManager,Spring Boot就是通过此类实现缓存的管理。redis对应此接口的实现类是org.springframework.data.redis.cache.RedisCacheManager。下面看看此类如何生成。
在配置文件application.properties的配置spring.redis.* 属性后,Spring Boot通过RedisAutoConfiguration来初始化RedisTemplate和StringRedisTemplate
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 @Configuration @ConditionalOnClass ({ JedisConnection.class, RedisOperations.class, Jedis.class })@EnableConfigurationProperties (RedisProperties.class)public class RedisAutoConfiguration { @Configuration protected static class RedisConfiguration { @Bean @ConditionalOnMissingBean (name = "redisTemplate" ) public RedisTemplate<Object, Object> redisTemplate ( RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>(); template.setConnectionFactory(redisConnectionFactory); return template; } @Bean @ConditionalOnMissingBean (StringRedisTemplate.class) public StringRedisTemplate stringRedisTemplate ( RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { StringRedisTemplate template = new StringRedisTemplate(); template.setConnectionFactory(redisConnectionFactory); return template; } } }
然后RedisCacheConfiguration会将RedisAutoConfiguration生成的RedisTemplate注入方法生成一个默认的RedisCacheManager 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 @Configuration @AutoConfigureAfter (RedisAutoConfiguration.class)@ConditionalOnBean (RedisTemplate.class)@ConditionalOnMissingBean (CacheManager.class)@Conditional (CacheCondition.class)class RedisCacheConfiguration { private final CacheProperties cacheProperties; private final CacheManagerCustomizers customizerInvoker; RedisCacheConfiguration(CacheProperties cacheProperties, CacheManagerCustomizers customizerInvoker) { this .cacheProperties = cacheProperties; this .customizerInvoker = customizerInvoker; } @Bean public RedisCacheManager cacheManager (RedisTemplate<Object, Object> redisTemplate) { RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate); cacheManager.setUsePrefix(true ); List<String> cacheNames = this .cacheProperties.getCacheNames(); if (!cacheNames.isEmpty()) { cacheManager.setCacheNames(cacheNames); } return this .customizerInvoker.customize(cacheManager); } }
自定义RedisCacheManager 如果CacheManager是由Spring Boot自动配置的,那么可以通过公开一个实现CacheManagerCustomizer接口的bean来进行其他配置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Bean public CacheManagerCustomizer<RedisCacheManager> redisManagerCustomizer () { return new CacheManagerCustomizer<RedisCacheManager>() { @Override public void customize (RedisCacheManager cacheManager) { cacheManager.setUsePrefix(true ); RedisCachePrefix cachePrefix = new DefaultRedisCachePrefix("redis_" ); cacheManager.setCachePrefix(cachePrefix ); cacheManager.setDefaultExpiration(24 *60 *60 ); } }; }