13.1、Spring学习—— 缓存相关的四个注解

Spring 和 缓存

缓存可以存储经常会用到的信息,这样每次需要的时候,这些信息都是立即可用的.
尽管Spring自身并没有实现缓存解决方案,但是它对缓存功能提供了声明式的支持,能够和如 ehcache、redis 实现集成.

实现机制

基于 Spring AOP
Spring AOP 通过 后置处理器的方式,在Spring bean 实例化阶段为该bean 生成对应的代理对象,从而实现功能增强的结果.

缓存注解的介绍

Spring 对缓存的实现有好几个方式,但是这几个方式都需要借助于几个基本的注解,这里对这几个注解进行介绍.
这些注解都是用于修饰方法的.

@Cacheable

该注解修饰的方法,表示对该方法的返回结果进行缓存,每次调用优先从缓存中寻找方法返回值,如果缓存不存在再调用该方法,此时返回值会被放入缓存中

适用场景:单纯查询方法的缓存

/**   
	 * value 是缓存规则的名字,可以指定多个,指定多个就会有多个缓存
	 * key 是缓存的名字,可以用 Spel 表达式直接从方法入参获取,当然也可以写为固定值
	 * condition 是条件判断,满足条件的才会被缓存
	 * sync 是否同步,true 为支持,Spring 4.3 后开始支持
	 */
	//@Cacheable(value = {"ehcache_test"},key = "#user.id",condition = "#user.id>100",sync=true)
	@Cacheable(value = {"ehcache_test"},key = "#user.id",condition = "#user.id>100")
	@Override
	public String aMethod(User user) {
		System.out.println("invoke aMethod");
		return Math.random()+"";

	}

@CacheEvict

该注解修饰的方法,在调用时会清除指定的缓存,可以一次指定多个

适用场景:单纯清除指定缓存规则、key的缓存

/**
	 * value 是缓存规则的名字,可以指定多个,指定多个就会有多个缓存
	 * key 缓存的名字,可以用 Spel 表达式直接从方法入参获取
	 * condition 是条件判断,满足条件的才会起作用
	 * allEntries 默认为只有 key 值匹配的才会被清楚缓存,设置为true则所有的缓存都失效,不可与key同时指定
	 * beforeInvocation 是否在方法调用前清楚缓存,默认为false 即只有方法完全调用成功才可以
	 */
	@CacheEvict(value = { "ehcache_test" },key = "#user.id",condition = "#user.id>100")
	@Override
	public void bMethod(User user) {
		System.out.println("invoke bMethod()");
	}

@CachePut

表明 Spring 应该将方法返回值放到缓存中.在方法的调用前并不会检查缓存,方法始终会被调用

适用场景:需要强制更新缓存内容的场景

/**
	 * value 是缓存规则的名字,可以指定多个,指定多个就会有多个缓存
	 * key 缓存的名字,可以用 Spel 表达式直接从方法入参获取
	 * condition 是条件判断,满足条件的才会起作用
	 */
	@CachePut(value = { "ehcache_test" },key = "#user.id",condition="#user.id>100")
	@Override
	public String cMethod(User user) {
		System.out.println("invoke cMethod");
		return Math.random()+"";
	}

@Caching

是一个分组的注解,能够同时应用多个其它的缓存注解

适用场景:需要一次配置多个缓存配置的情况——这种场景多见于一次修改导致多个缓存失效的情况

/**
	 * @Caching 可以同时配置多个@Cacheable、@CacheEvict、@CachePut
	 * 内部规则就是所有分规则的集合
	 * 需要注意的是
	 */
	@Caching(cacheable = {@Cacheable(value={"ehcache_test"},key="#p0.id"),@Cacheable(value={"ehcache_test"},key="#p0.id")},
			evict = {@CacheEvict(value={"ehcache_test","demo"},key= "#p0.id")},
			put = {@CachePut(value={"ehcache_test"},key="#p0.id")})
	@Override
	public String dMethod(User user) {
		System.out.println("invoke dMethod");
		return Math.random()+"";
	}

缓存注解的相关问题

key 值的取值

源码中有如下内容,key值可由Spel表达式动态的获取,默认没有设置的情况下,会将方法中所有的参数作为key

/**
	 * Spring Expression Language (SpEL) attribute for computing the key dynamically.
	 * <p>Default is "", meaning all method parameters are considered as a key.
	 */
	String key() default "";

具体选择来说,可以分为三类,基本涵盖了 方法入参、方法名、方法类

  • 第一类是直接以 #方法入参(或子属性)作为key
    key="#id"
    key="#user.id"

  • 第二类是使用Spel的 #p0(或子属性) 作为key
    key="#p0"
    key="#p0.id"

  • 第三类是使用 #root 相关属性作为key,可以将“#root”省略,因为Spring默认使用的就是root对象的属性

属性名称描述示例
methodName当前方法名#root.methodName
method当前方法#root.method.name
target当前被调用的对象#root.target
targetClass当前被调用的对象的class#root.targetClass
args当前方法参数组成的数组#root.args[0]
caches当前被调用的方法使用的Cache#root.caches[0].name

并发问题

一般而言,缓存都是用于缓解数据库的压力,这样一来,当缓存为空,而高并发查询发生时,可能会瞬间搞跨数据库,这个 问题怎么解决?
Spring 4.3 开始,在 @Cachable 注解中增加了 sync 属性,当sync=true 时,只有一次方法调用结束后才会允许其它访问进来,从而避免了对数据库对并发访问

/**
	 * @Cacheable 适用场景:单纯查询方法的缓存
	 * value 是缓存规则的名字,可以指定多个,指定多个就会有多个缓存
	 * key 是缓存的名字,可以用 Spel 表达式直接从方法入参获取
	 * condition 是条件判断,满足条件的才会被缓存
	 */
	@Cacheable(value = {"ehcache_test"},key = "#user.id",condition = "#user.id>100",sync=true)
	@Override
	public String aMethod(User user) {
		System.out.println("invoke aMethod");
		return Math.random()+"";
	}

注解使用案例

这里在未来会逐步替换为各具体场景的案例

/**
	 * @Cacheable 适用场景:单纯查询方法的缓存
	 * value 是缓存规则的名字,可以指定多个,指定多个就会有多个缓存
	 * key 是缓存的名字,可以用 Spel 表达式直接从方法入参获取
	 * condition 是条件判断,满足条件的才会被缓存
	 * sync 是否同步,true 为支持,Spring 4.3 后开始支持
	 */
	//@Cacheable(value = {"ehcache_test"},key = "#user.id",condition = "#user.id>100",sync=true)
	//@Cacheable(value = {"ehcache_test"},key = "#user.id",condition = "#user.id>100")
	@Cacheable(value = {"ehcache_test"},key = "#p0.id",condition = "#p0.id>100")
	@Override
	public String aMethod(User user) {
		System.out.println("invoke aMethod");
		return Math.random()+"";
	}
	
	
	/**
	 * @CacheEvict 适用场景:该注解修饰的方法,在调用时会清除指定的缓存,可以一次指定多个
	 * value 是缓存规则的名字,可以指定多个,指定多个就会有多个缓存
	 * key 缓存的名字,可以用 Spel 表达式直接从方法入参获取
	 * condition 是条件判断,满足条件的才会起作用
	 * allEntries 默认为只有 key 值匹配的才会被清楚缓存,设置为true则所有的缓存都失效,不可与key同时指定
	 * beforeInvocation 是否在方法调用前清楚缓存,默认为false 即只有方法完全调用成功才可以
	 */
	@CacheEvict(value = { "ehcache_test" },key = "#user.id",condition = "#user.id>100")
	@Override
	public void bMethod(User user) {
		System.out.println("invoke bMethod()");
	}
	
	/**
	 * @CachePut 适用场景
	 * value 是缓存规则的名字,可以指定多个,指定多个就会有多个缓存
	 * key 缓存的名字,可以用 Spel 表达式直接从方法入参获取
	 * condition 是条件判断,满足条件的才会起作用
	 */
	@CachePut(value = { "ehcache_test" },key = "#user.id",condition="#user.id>100")
	@Override
	public String cMethod(User user) {
		System.out.println("invoke cMethod");
		return Math.random()+"";
	}

	/**
	 * 这个方法看看就行
	 * @Caching 可以同时配置多个@Cacheable、@CacheEvict、@CachePut
	 * 内部规则就是所有分规则的集合
	 * 需要注意的是
	 */
	@Caching(cacheable = {@Cacheable(value={"ehcache_test"},key="#p0.id"),@Cacheable(value={"ehcache_test"},key="#p0.id")},
			evict = {@CacheEvict(value={"ehcache_test","demo"},key= "#p0.id")},
			put = {@CachePut(value={"ehcache_test"},key="#p0.id")})
	@Override
	public String dMethod(User user) {
		System.out.println("invoke dMethod");
		return Math.random()+"";
	}
	
	/**
	 * #root.method.name 可以省略 #root.
	 */
	//@Cacheable(value="ehcache_test",key="#root.method.name")
	@Cacheable(value="ehcache_test",key="method.name")
	@Override
	public String eMethod(Long id) {
		System.out.println("invoke eMethod");
		return id+"";
	}
	
	/**
	 * #root.caches[index] 可以省略 #root
	 * #root.args[index] 可以省略 #root
	 */
	//@Cacheable(value="ehcache_test",key="caches[0]")
	@Cacheable(value="ehcache_test",key="args[0]")
	@Override
	public String fMethod(Long id) {
		System.out.println("invoke fMethod");
		return id+"";
	}

参考资料

[1]、https://blog.csdn.net/newbie_907486852/article/details/81478046 基础概念
[2]《Spring 实战 4》基础概念和项目整合
[3]、https://www.cnblogs.com/fashflying/p/6908028.html 基础概念
[4]、https://blog.csdn.net/clementad/article/details/52452119 并发问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值