1.@CachePut注解
当缓存需要在不干扰方法执行的情况下被更新,我们使用该注解.
方法总是会被调用,它的结果会替换缓存钟得值(根据 @CachePut的选项). 注解提供了类似@Cacheable的选项,并且它应该被用来填充缓存而不是优化方法流程.
下面是使用方法
1 2 | @CachePut(cacheNames="book", key="#isbn") public Book updateBook(ISBN isbn, BookDescriptor descriptor) |
强烈反对在同一个方法上使用 @CachePut和@Cacheable.因为他们是不同的行为.后者导致使用缓存而跳过方法调用,前者又会调用方法更新缓存.这导致了异常的行为, 除了一些特殊案例(比如注解有排除彼此的条件),应当被避免. 另外这些条件应该避免依赖 结果对象(#result 变量),因为他们是预先验证的,以确保排除
2.@CacheEvice 注解
缓存抽象不仅可以存储缓存也可以删除缓存. 在数据不再使用或者过期的时候删除缓存.强烈反对同事使用 @Cacheable和@CacheEvict.
@CacheEvice需要指定一个或者多个需要操作的缓存,允许指定一个自定义缓存和key resolution或者一个条件.对于这个注解 有个额外的参数(allEntries),用来指定一个缓存范围删除而不是仅仅一个缓存实体(基于key).
1 2 | @CacheEvict(cacheNames="books", allEntries=true) public void loadBooks(InputStream batch) |
上面的例子 使用 allEntries=true 来删除所有在缓存中的实体.
这个选项在需要清除整个缓存区域的时候非常方便.对比每个清除每个实体(耗时,效率低),上面的操作,所有实体通过一个操作被删除.这种场景下框架会忽略所有的键指定.
当然我们可以指定缓存是在方法调用后(默认)还是方法调用前删除,通过 beforeInvocation。
当值为false时,方法没有执行(也许被缓存了)或者抛出了一个异常,缓存将不会被删除.
反之则永远会被执行,这在缓存不需要和方法绑定时很有用.
注意的是 无返回值的方法也可以使用 @CacheEvict.
3.@Caching 注解
有时需要指定多个同一个类型的注解(比如@CacheEvict 或者 @CachePut),比如一些条件或者key表达式在不同缓存之间不同.@Caching 允许多个注解嵌套在一个方法上
1 2 | @Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames="secondary", key="#p0") }) public Book importBooks(String deposit, Date date) |
4.@CacheConfig 注解
通过这个注解,我们可以将一些配置在所有类操作上的繁琐操作简化.
比如:
1 2 3 4 5 6 | @CacheConfig("books") public class BookRepositoryImpl implements BookRepository { @Cacheable public Book findBook(ISBN isbn) {...} } |
通过注解我们定义了相同的缓存.
@CacheConfig是一个 允许共享缓存名称的类级别的注解,可以定义 KeyGenerator,CancheManager和CacheResolver。
将该注解放到类上并不会打开任何缓存操作.
一个操作级别的定制会覆盖 @CacheConfig的自定义集合.因此给出三个级别的定制:
- 全局配置, 对 CacheManager,KeyGenerator可用
- 类级别,使用@CacheConfig
- 操作级别
方法可见度和缓存注解
当我们使用代理的时候,应该在public 方法上使用缓存注,如果你在protected,private,或者package可见度下使用注解,不会有异常,但是已配置的缓存配置不会生效. 使用AspectJ如果你使用 非public方法,因为它可以从字节码改变.
Spring推荐,用@Cache*注解 注解在正确的类(或者正确的类方法上), 反对注释到接口上.当然我们能够 把@Cache*注解放到接口或者接口的方法上 ,但是这只能在你使用基于接口的代理上工作. 事实上java注解不能够继承接口意味着如果我们使用的基于类的代理(proxy-target- class="true") 或者基于aspect(mode="aspectj"),缓存设置不会被代理和编织的基础构建认出,这样对象就没有被缓存代理给修饰。
5.自定义注解
自定义注解和 A spectJ
这个功能是基于代理完成的,也可以通过使用Aspectj做一些额外的工作来开启. spring-asepcts 模块仅为标砖注解定义了一个切面.如果需要定义自己的注解,需要为了这个自己定义一个切面。 参考 AnnotationCacheAspect
缓存抽象可以让我们使用自己的注解来定义哪些方法需要触发放入或移除. 作为一个模板机制是非常便利的, 可以去掉重复的注解声明,特别是在key或者条件的指定或者代码库不允许外部引用(org.springframework)。 于其他的sterotype注解类似,可以使用@Cacheable,@CachePut,@CacheEvict,和@CacheConfig 来作为 元注解(meta-mannotations).
1 2 3 4 5 | @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) @Cacheable(cacheNames = {"coffees","coffees2"}, key = "#name") public @interface CoffeeCache { } |
上面的例子用公用的@Cacheable声明在自定义的注解上.
接下来就可以改变代码的注释
1 2 3 4 | @Cacheable({"coffees","coffees2"}) public List<Coffee> findAllCoffee() { return coffeeRepository.findAll(); } |
即使@CoffeeCache不是Spring原生注解,我们也能够被容器在runtime中发现并且理解它的意思. 但是annotation-driven 的行为必须被启用.
1 2 3 4 | @CoffeeCache public List<Coffee> findAllCoffee() { return coffeeRepository.findAll(); } |