Spring缓存

Spring中的缓存抽象

Spring Cache 是 Spring 提供的一整套的缓存解决方案。虽然它本身并没有提供缓存的实现,但是它提供了一整套的接口和代码规范、配置、注解等,这样它就可以整合各种缓存方案了,比如 Redis、Ehcache,我们也就不用关心操作缓存的细节。

Spring 3.1 开始定义了 org.springframework.cache.Cache 和 org.springframework.cache.CacheManager 接口来统一不同的缓存技术,并支持使用注解来简化我们开发。

Cache 接口它包含了缓存的各种操作方式,同时还提供了各种xxxCache缓存的实现,比如 RedisCache 针对Redis,EhCacheCache 针对 EhCache,ConcurrentMapCache 针对 ConcurrentMap

Spring Cache 常用注解

Spring提供了四个注解来声明缓存规则:@Cacheable,@CachePut,@CacheEvict,@Caching

注解 描述
@Cacheable 在调用方法前,首先去缓存中招方法的返回值,如果能找到,则返回缓存的值,否则就执行这个方法,并将返回值放入缓存中
@CachePut 在方法调用前不会去缓存中招,无论如何都会执行执行方法,执行完后将返回值放入缓存中
@CacheEvict 清理缓存中的一个或者多个记录
@Caching 能够同时应用多个缓存注解
@CacheConfig 在类级别共享相同的缓存配置

SpringEL上下文对象

名称 位置 描述 示例
methodName root对象 当前被调用的方法名 #root.methodname
method root对象 当前被调用的方法 #root.method.name
target root对象 当前被调用的目标对象实例 #root.target
targetClass root对象 当前被调用的目标对象的类 #root.targetClass
args root对象 当前被调用的方法的参数列表 #root.args0
caches root对象 当前方法调用使用的缓存列表 #root.caches0.name
Argument Name 执行上下文 当前被调用的方法的参数,如findArtisan(Artisan artisan),可以通过#artsian.id获得参数 #artsian.id
result 执行上下文 方法执行后的返回值(仅当方法执行后的判断有效,如 unless cacheEvict的beforeInvocation=false) #result

源码解析

首先看下EnableCaching注解,引入了CachingConfigurationSelector配置类

java 复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching {

	/**
	 * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
	 * to standard Java interface-based proxies. The default is {@code false}. <strong>
	 * Applicable only if {@link #mode()} is set to {@link AdviceMode#PROXY}</strong>.
	 * <p>Note that setting this attribute to {@code true} will affect <em>all</em>
	 * Spring-managed beans requiring proxying, not just those marked with {@code @Cacheable}.
	 * For example, other beans marked with Spring's {@code @Transactional} annotation will
	 * be upgraded to subclass proxying at the same time. This approach has no negative
	 * impact in practice unless one is explicitly expecting one type of proxy vs another,
	 * e.g. in tests.
	 */
	boolean proxyTargetClass() default false;

	/**
	 * Indicate how caching advice should be applied.
	 * <p><b>The default is {@link AdviceMode#PROXY}.</b>
	 * Please note that proxy mode allows for interception of calls through the proxy
	 * only. Local calls within the same class cannot get intercepted that way;
	 * a caching annotation on such a method within a local call will be ignored
	 * since Spring's interceptor does not even kick in for such a runtime scenario.
	 * For a more advanced mode of interception, consider switching this to
	 * {@link AdviceMode#ASPECTJ}.
	 */
	AdviceMode mode() default AdviceMode.PROXY;

	/**
	 * Indicate the ordering of the execution of the caching advisor
	 * when multiple advices are applied at a specific joinpoint.
	 * <p>The default is {@link Ordered#LOWEST_PRECEDENCE}.
	 */
	int order() default Ordered.LOWEST_PRECEDENCE;

}

再看下CachingConfigurationSelector类,默认为PROXY模式,会加载AutoProxyRegistrar和ProxyCachingConfiguration两个配置。

java 复制代码
public class CachingConfigurationSelector extends AdviceModeImportSelector<EnableCaching> {


	/**
	 * Returns {@link ProxyCachingConfiguration} or {@code AspectJCachingConfiguration}
	 * for {@code PROXY} and {@code ASPECTJ} values of {@link EnableCaching#mode()},
	 * respectively. Potentially includes corresponding JCache configuration as well.
	 */
	@Override
	public String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return getProxyImports();
			case ASPECTJ:
				return getAspectJImports();
			default:
				return null;
		}
	}

	/**
	 * Return the imports to use if the {@link AdviceMode} is set to {@link AdviceMode#PROXY}.
	 * <p>Take care of adding the necessary JSR-107 import if it is available.
	 */
	private String[] getProxyImports() {
		List<String> result = new ArrayList<>(3);
		result.add(AutoProxyRegistrar.class.getName());
		result.add(ProxyCachingConfiguration.class.getName());
		if (jsr107Present && jcacheImplPresent) {
			result.add(PROXY_JCACHE_CONFIGURATION_CLASS);
		}
		return StringUtils.toStringArray(result);
	}
}

再看看ProxyCachingConfiguration类中,发现这个类定义了缓存的拦截器

java 复制代码
	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public CacheInterceptor cacheInterceptor(CacheOperationSource cacheOperationSource) {
		CacheInterceptor interceptor = new CacheInterceptor();
		interceptor.configure(this.errorHandler, this.keyGenerator, this.cacheResolver, this.cacheManager);
		interceptor.setCacheOperationSource(cacheOperationSource);
		return interceptor;
	}

在父类AbstractCachingConfiguration中还处理了用户自定义的缓存属性

java 复制代码
	@Autowired
	void setConfigurers(ObjectProvider<CachingConfigurer> configurers) {
		Supplier<CachingConfigurer> configurer = () -> {
			List<CachingConfigurer> candidates = configurers.stream().collect(Collectors.toList());
			if (CollectionUtils.isEmpty(candidates)) {
				return null;
			}
			if (candidates.size() > 1) {
				throw new IllegalStateException(candidates.size() + " implementations of " +
						"CachingConfigurer were found when only 1 was expected. " +
						"Refactor the configuration such that CachingConfigurer is " +
						"implemented only once or not at all.");
			}
			return candidates.get(0);
		};
		useCachingConfigurer(new CachingConfigurerSupplier(configurer));
	}

	/**
	 * Extract the configuration from the nominated {@link CachingConfigurer}.
	 */
	protected void useCachingConfigurer(CachingConfigurerSupplier cachingConfigurerSupplier) {
		this.cacheManager = cachingConfigurerSupplier.adapt(CachingConfigurer::cacheManager);
		this.cacheResolver = cachingConfigurerSupplier.adapt(CachingConfigurer::cacheResolver);
		this.keyGenerator = cachingConfigurerSupplier.adapt(CachingConfigurer::keyGenerator);
		this.errorHandler = cachingConfigurerSupplier.adapt(CachingConfigurer::errorHandler);
	}
相关推荐
于指尖飞舞6 小时前
java面试题总结
java·开发语言
ZealSinger7 小时前
LangChain4j用AiServices拼Tool
java·rag·ai智能体·langchain4j
vx_Biye_Design7 小时前
springboot高校选课系统82776-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·spring·课程设计·express
Sweet锦8 小时前
不调 Python,不装向量库:我用纯 Java 写了一套以图搜图引擎
java·人工智能·开源·图搜索
如意猴9 小时前
【C++】007 C/C++ 内存管理机制、malloc与new的区别及模板初阶
java·c++·算法
可乐鸡翅yeah_9 小时前
HLS 分片过期清理,直播旧 TS 分片磁盘爆满问题处理
java·后端·spring·m3u8·m3u8在线·音视频在线播放
专业程序开发源9 小时前
SSM校园拍摄交流服务平台36936-计算机课程设计、毕业设计
java·spring boot·后端·python·elasticsearch·php·课程设计
用户094248568039 小时前
第23章:OpenJDK逃逸分析、标量替换与锁优化
java·jvm
wuminyu9 小时前
LockStack在虚拟线程Mount和Unmount拷贝过程剖析
java·linux·c语言·jvm·c++
樱花落木兰10 小时前
分布式登录实战:Session 会话共享改造,Redis 存储用户登录状态
java·javascript·数据库·redis·分布式·缓存