Spring基于注解的缓存声明深入探究

目录
  • 一、概述
  • 二、声明式基于注解的缓存
    • 1、@Cacheable注解
      • (1) 默认缓存key的生成
      • (2) 声明式自定义key生成
      • (3) 默认缓存解析
      • (4) 自定义缓存解析
      • (5) 条件式缓存
    • 2、@CachePut注解
    • 3、@CacheEvict注解
    • 4、@Caching注解
    • 5、@CacheConfig注解
  • 三、开启声明式缓存注解
  • 四、使用自定义注解

一、概述

从3.1版本起,Spring框架就已经支持将缓存添加到现有的Spring应用中,和事务支持一样,缓存抽象允许在对代码影响最小的情况下一致性地使用各种缓存解决方案。

从Spring 4.1版本起,有了JSR-107注解和更多定制化的选项支持后,缓存抽象有了重大的改进。

二、声明式基于注解的缓存

对于缓存声明,该抽象提供了一套Java注解:

  • @Cacheable:触发缓存构建。
  • @CacheEvict:触发缓存销毁。
  • @CachePut:更新缓存。
  • @Caching:重组应用到方法上的多个缓存操作。
  • @CacheConfig:类级别共享缓存相关的通用设置。

1、@Cacheable注解

正如其名,@Cacheable注解用来区分方法执行结果是否应该被缓存,如果后续该方法再次被调用,方法的执行结果直接从缓存中获取,而不会调用实际的方法逻辑。示例如下:

@Cacheable("books")
public Book findBook(ISBN isbn) {...}

当然我们也可以指定多个缓存名称,如果至少一个缓存被命中,那么关联的缓存结果就会返回,示例如下:

@Cacheable({"books", "isbns"})
public Book findBook(ISBN isbn) {...}

(1) 默认缓存key的生成

因为缓存都是key-value存储,每次缓存方法的调用都会被转义为缓存key的访问。Spring缓存抽象对于key的生成会采用KeyGenerator来生成,算法如下:

  • 如果没有方法参数,返回SimpleKey.EMPTY
  • 如果该方法只有一个参数,返回参数实例。
  • 如果方法不止一个参数,返回包含所有参数的SimpleKey实例。

这种key的生成策略适用于大部分场景,只要方法参数合理实现了hashCode()equals()方法。

SimpleKeyGenerator源码如下:

public class SimpleKeyGenerator implements KeyGenerator {
	@Override
	public Object generate(Object target, Method method, Object... params) {
		return generateKey(params);
	}
	/**
	 * Generate a key based on the specified parameters.
	 */
	public static Object generateKey(Object... params) {
		if (params.length == 0) {
			return SimpleKey.EMPTY;
		}
		if (params.length == 1) {
			Object param = params[0];
			if (param != null && !param.getClass().isArray()) {
				return param;
			}
		}
		return new SimpleKey(params);
	}
}

备注:如果要实现自定义key生成策略,需要实现org.springframework.cache.interceptor.KeyGenerator接口。

(2) 声明式自定义key生成

目标方法可能会有多个参数,有些参数可能只应用于方法逻辑,而不适合用作key的生成,例如:

@Cacheable("books")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

对于这种情况,@Cacheable注解有一个key属性,通过该属性可以自定义Key生成。我们也可以使用SPEL(Spring表达式语言)去指定参数或者参数的嵌套属性,示例如下:

@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)

当然,我们也可以通过@Cacheable注解指定自定义的KeyGenerator实例,示例如下:

@Cacheable(cacheNames="books", keyGenerator="myKeyGenerator")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

备注:keykeyGenerator参数是互斥的,如果两者都指定会触发异常。

(3) 默认缓存解析

Spring缓存抽象通过CacheResolver去解析操作级别的缓存,而CacheResolver会用CacheManager去获取缓存,接口定义如下:

@FunctionalInterface
public interface CacheResolver {
	/**
	 * Return the cache(s) to use for the specified invocation.
	 * @param context the context of the particular invocation
	 * @return the cache(s) to use (never {@code null})
	 * @throws IllegalStateException if cache resolution failed
	 */
	Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context);
}
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();
}

(4) 自定义缓存解析

默认缓存解析对于单CacheManager应用适应很好,对于有多个缓存管理器的应用,我们可以对每个操作设置缓存管理器,如下:

@Cacheable(cacheNames="books", cacheManager="anotherCacheManager")
public Book findBook(ISBN isbn) {...}

(5) 条件式缓存

有时方法缓存结果可能要取决于指定的参数,缓存注解通过支持SPELcondition属性实现该功能,示例如下:

@Cacheable(cacheNames="book", condition="#name.length() < 32")
public Book findBook(String name)

备注:只有当参数name的长度小于32时,方法结果才会被缓存。

除了condition属性,unless属性可以用来决定方法返回值不缓存。与condition不同,unless表达式在方法被调用后才会执行,示例如下:

@Cacheable(cacheNames="book", condition="#name.length() < 32", unless="#result.hardback")
public Book findBook(String name)

备注:如果Book对象的hardback属性为true则不缓存,为false才缓存。

当然,缓存抽象同时也支持java.util.Optional,只有当Optional中的值存在时,方法返回值才会被缓存。#result代表方法的执行结果,上面的我们可以改写:

@Cacheable(cacheNames="book", condition="#name.length() < 32", unless="#result?.hardback")
public Optional<Book> findBook(String name)

备注;#result引用的始终是Book对象,而不是Optional对象,因为返回值可能为空,所以我们应该使用安全导航操作符 => ?.

关于其它可以用的缓存SpEL表达式上下文,可以参考:Available Caching SpEL Evaluation Context

2、@CachePut注解

这个注解主要用于更新缓存,也就说带有该注解的方法总是会执行,并且方法的返回值会刷新缓存。该注解和@Cacheable的参数相同,示例如下:

@CachePut(cacheNames="book", key="#isbn")
public Book updateBook(ISBN isbn, BookDescriptor descriptor)

备注:@CachePut@Cacheable的主要区别在于后者会通过缓存跳过方法的执行,而前者为了更新缓存会迫使方法执行。

3、@CacheEvict注解

这个注解主要用来清除缓存,与@Cacheable注解相反,方法的执行会触发从缓存中删除数据。@CacheEvit注解要求指定一个或多个缓存名。除此之外,该注解还有一个额外的属性allEntries,指定该属性值为true后会清除某个缓存名下的所有缓存key。示例如下:

@CacheEvict(cacheNames="books", allEntries=true)
public void loadBooks(InputStream batch)

备注:缓存名为books下的所有缓存key都会被清除。

4、@Caching注解

有些情况下,相同类型多个注解,如@CacheEvict或者@CachePut需要被指定。@Caching注解允许多个嵌套@Cacheable@CachePut@CacheEvict注解用在同一个方法上。示例如下:

@Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames="secondary", key="#p0") })
public Book importBooks(String deposit, Date date)

5、@CacheConfig注解

目前我们已经了解到缓存操作提供了很多定制化的选项,然而有些定制化选项如果应用到类中的所有操作可能会有些冗余,示例如下:

@CacheConfig("books")
public class BookRepositoryImpl implements BookRepository {
	@Cacheable
	public Book findBook(ISBN isbn) {...}
}

@CacheConfig是一个类级别的注解,这个注解可以共享缓存名称,自定义的KeyGenerator,自定义的CacheManager和自定义的CacheResolver

方法操作级别的自定义选项总是会重写@CacheConfig中的自定义选项。下面是每个缓存操作自定义选项对应的3个级别,优先级从上至下越来越高。

  • 全局配置的CacheManagerKeyGenerator等。
  • 类级别,通过@CacheConfig指定。
  • 方法操作级别。

三、开启声明式缓存注解

直接在配置类上加上#EnableCaching即可,如下:

@Configuration
@EnableCaching
public class AppConfig {
}

四、使用自定义注解

Spring缓存抽象允许我们用自定义注解去标识什么方法可以触发缓存构建或者消除。@Cacheable, @CachePut, @CacheEvict and @CacheConfig这些注解都可以作为元注解,其实即使可以修饰其它注解,示例如下:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Cacheable(cacheNames="books", key="#isbn")
public @interface SlowService {
}

上面我们自定义了SlowService注解,该注解被@Cacheable所修饰,现在我们可以用自定义注解代替如下代码:

@Cacheable(cacheNames="books", key="#isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

替代代码如下:

@SlowService
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

尽管@SlowService注解并不是Spring原生注解,但Spring容器会在运行时识别并且知道它是用来干嘛的。

备注:后面我们会利用自定义注解实现自定义过期时间的缓存方案。

到此这篇关于Spring基于注解的缓存声明深入探究的文章就介绍到这了,更多相关Spring注解的缓存内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Spring Boot 基于注解的 Redis 缓存使用详解

    看文本之前,请先确定你看过上一篇文章<Spring Boot Redis 集成配置>并保证 Redis 集成后正常可用,因为本文是基于上文继续增加的代码. 一.创建 Caching 配置类 RedisKeys.Java package com.shanhy.example.redis; import java.util.HashMap; import java.util.Map; import javax.annotation.PostConstruct; import org.springf

  • SpringBoot日志注解与缓存优化详解

    目录 日志注解: 缓存的优化: 总结 日志注解: 关于SpringBoot中的日志处理,在之前的文章中页写过: 点击进入 这次通过注解+Aop的方式来实现日志的输出: 首先需要定义一个注解类:  @Target(ElementType.METHOD)  @Retention(RetentionPolicy.RUNTIME)  @Documented  public @interface LogAnnotation {      String module() default "";  

  • 详解Spring缓存注解@Cacheable,@CachePut , @CacheEvict使用

    注释介绍 @Cacheable @Cacheable 的作用 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存 @Cacheable 作用和配置方法 参数 解释 example value 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 例如: @Cacheable(value="mycache") @Cacheable(value={"cache1","cache2"} key 缓存的 key,可以为空,如果指定要按照

  • spring boot注解方式使用redis缓存操作示例

    本文实例讲述了spring boot注解方式使用redis缓存操作.分享给大家供大家参考,具体如下: 引入依赖库 在pom中引入依赖库,如下 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> &l

  • Spring AOP如何整合redis(注解方式)实现缓存统一管理详解

    前言 项目使用redis作为缓存数据,但面临着问题,比如,项目A,项目B都用到redis,而且用的redis都是一套集群,这样会带来一些问题. 问题:比如项目A的开发人员,要缓存一些热门数据,想到了redis,于是乎把数据放入到了redis,自定义一个缓存key:hot_data_key,数据格式是项目A自己的数据格式,项目B也遇到了同样的问题,也要缓存热门数据,也是hot_data_key,数据格式是项目B是自己的数据格式,由于用的都是同一套redis集群,这样key就是同一个key,有的数据

  • springboot增加注解缓存@Cacheable的实现

    目录 springboot增加注解缓存@Cacheable 业务层使用 配置 @Cacheable注解的属性使用 cacheNames和value key keyGenerator keyGenerator condition unless(除非) sync springboot增加注解缓存@Cacheable 业务层使用 @Cacheable(value = "dictionary#1800", key = "#root.targetClass.simpleName +':

  • Spring缓存注解@Cacheable @CacheEvit @CachePut使用介绍

    目录 I. 项目环境 1. 项目依赖 II. 缓存注解介绍 1. @Cacheable 2. @CachePut 3. @CacheEvict 4. @Caching 5. 异常时,缓存会怎样? 6. 测试用例 7. 小结 III. 不能错过的源码和相关知识点 0. 项目 Spring在3.1版本,就提供了一条基于注解的缓存策略,实际使用起来还是很丝滑的,本文将针对几个常用的注解进行简单的介绍说明,有需要的小伙伴可以尝试一下 本文主要知识点: @Cacheable: 缓存存在,则使用缓存:不存在

  • SpringBoot学习之基于注解的缓存

    目录 主要使用到的注解: 使用步骤 注解属性介绍 @Cacheable() 的属性值及释义: @CachPut属性值及释义: @CachEvict属性值及释义: @Caching 总结 主要使用到的注解: @Cacheable(放入缓存) 能够根据方法的请求参数对其结果进行缓存 @CachePut(修改缓存中的值) 能够根据方法的请求参数对其结果进行缓存,和 @Cacheable 不同的是,它每次都会触发真实方法的调用 @CachEvict(清除缓存) 能够根据一定的条件对缓存进行清空 使用步骤

  • Spring基于注解的缓存声明深入探究

    目录 一.概述 二.声明式基于注解的缓存 1.@Cacheable注解 (1) 默认缓存key的生成 (2) 声明式自定义key生成 (3) 默认缓存解析 (4) 自定义缓存解析 (5) 条件式缓存 2.@CachePut注解 3.@CacheEvict注解 4.@Caching注解 5.@CacheConfig注解 三.开启声明式缓存注解 四.使用自定义注解 一.概述 从3.1版本起,Spring框架就已经支持将缓存添加到现有的Spring应用中,和事务支持一样,缓存抽象允许在对代码影响最小的

  • Spring基于注解整合Redis完整实例

    在<Redis之--Spring整合Redis>一文中,向大家介绍了如何将spring与Redis整合起来,但不是基于注解的形式,很多同学都希望能够通过注解的形式来简单的将Spring与Redis整合起来,这样,在使用的时候,只需要在相应的方法上加上注解,便可以使方法轻松的调用Redis的缓存.那么今天就来向大家介绍如何用基于注解的形式来整合Spring与Redis. 一.项目搭建 今天,我们不使用hibernate来操作数据库了,我们今天选择的框架是: Spring4(包括mvc.conte

  • Spring基于注解配置事务的属性

    本文实例为大家分享了Spring基于注解配置事务的属性,供大家参考,具体内容如下 一.事务属性概述 在Spring中,事务属性描述了事务策略如何应用到方法上,事务属性包含5个方面: ① 传播行为② 隔离级别③ 回滚策略④ 超时时间⑤ 是否只读 二.事务的传播行为属性## 1.当事务方法被另一个事务方法调用时,必须指定事务应该如何传播.例如,方法可能继续在现有的事务中允许,也可能开启一个新事务,并在自己的事务中运行.2.事务的传播行为可以由传播属性指定,Spring定义了7种类型的传播行为.其中最

  • 浅析Spring基于注解的AOP

    目录 一.准备工作 二.基于注解的AOP之前置通知 三.基于注解的AOP之切入点表达式的语法和重用以及获取连接点的信息 ①切入点表达式的语法 ②获取连接点的信息 ③重用写入点表达式 一.准备工作 ①创建一个Maven工程 ②添加依赖 在IOC所需依赖基础上再加入下面依赖即可: <!-- spring-aspects会帮我们传递过来aspectjweaver --> <dependency> <groupId>org.springframework</groupId

  • spring基于注解配置实现事务控制操作

    目录 spring注解配置实现事务控制 1.导入相关依赖 2.创建spring配置类 3.创建JdbcConfig数据源配置类 4.创建TransactionConfig事务配置类 5.创建jdbcConfig.properties 6.使用事务注解 Spring注解方式的事务实现机制 1.事务的实现机制 AOP动态代理进行方法拦截 事务管理器进行事务提交或回滚 2.注解方式的事务使用注意事项 正确的设置 @Transactional 的 propagation 属性(熟知事务的传播特性) 正确

  • spring cache注解@Cacheable缓存穿透详解

    目录 具体注解是这样的 基于这个思路我把Cache的实现改造了一下 取缓存的get方法实现 测试了一下,发现ok了 最近发现线上监控有个SQL调用量很大,但是方法的调用量不是很大,查看接口实现,发现接口是做了缓存操作的,使用Spring cache缓存注解结合tair实现缓存操作. 但是为啥SQL调用量这么大,难道缓存没有生效.测试发现缓存是正常的,分析了代码发现,代码存在缓存穿透的风险. 具体注解是这样的 @Cacheable(value = "storeDeliveryCoverage&qu

  • Spring基于注解读取外部配置文件

    一.使用注解@PropertySource 指定路径 使用 @PropertySource 指定配置文件路径,支持 properties 和 XML 的配置文件,但不支持 yml. 属性赋值 可以用注解 @Value 对属性直接赋值.${}获取配置文件的值.SPEL表达式#{}. 直接赋值:@Value("name jack") 读取配置文件:@Value("${user.age}") 指定默认值:@Value("${user.desc:default d

  • Spring IOC基于注解启动示例详析

    Spring 基于注解启动 主要有两个Class实现注解启动 AnnotationConfigApplicationContext AnnotationConfigWebApplicationContext 我们以AnnotationConfigApplicationContext 为研究对象 AnnotationConfigApplicationContext.png 引入Spring 最小依赖 <dependency> <groupId>org.springframework&

  • SpringBoot详解整合Spring Cache实现Redis缓存流程

    目录 1.简介 2.常用注解 2.1.@EnableCaching 2.2.@Cacheable 2.3.@CachePut 2.4.@CacheEvict 3.使用Redis当作缓存产品 3.1.坐标导入 3.2.yml配置 3.3.开启注解功能 3.4.使用@Cacheable 3.5.使用@CacheEvict 4.测试 1.简介 Spring Cache 是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能. Spring Cache 提供了一层抽象,底层可以切

随机推荐