西安市城乡建设管理局网站,做珠宝网站公司,惠州网站策划建设,wordpress评论qqSpring 的声明式缓存注解#xff08;Cacheable, CachePut, CacheEvict 等#xff09;是 AOP 技术在实际应用中最强大、最经典的范例之一#xff0c;其原理与 Transactional 非常相似。
核心思想#xff1a;一个智能的“秘书”
你可以把 Cacheable 的 AOP 实现想象成一个极…Spring 的声明式缓存注解Cacheable, CachePut, CacheEvict 等是 AOP 技术在实际应用中最强大、最经典的范例之一其原理与 Transactional 非常相似。
核心思想一个智能的“秘书”
你可以把 Cacheable 的 AOP 实现想象成一个极其高效的“秘书”。
你 (调用方)老板。目标方法 (findProductById): 一个需要花费大量时间和精力去调研才能回答的“问题”比如去档案馆查资料。AOP 代理 (Proxy): 你的秘书。缓存 (Cache): 秘书的“备忘录”或“笔记”。
当你向秘书提出一个问题时调用方法
秘书首先会翻看自己的备忘录查缓存。如果备忘录里有答案缓存命中秘书会直接告诉你答案然后回去继续做其他事。他根本不会去档案馆进行那次耗时又费力的调研。你作为老板很快就得到了答案甚至不知道秘书是直接给的还是去查了资料。如果备忘录里没有答案缓存未命中秘书会告诉你“老板请稍等我需要去查一下。” 然后他会亲自去档案馆进行调研执行目标方法。当他拿到调研结果后他会非常聪明地先把结果记在自己的备忘录里放入缓存以备你下次再问。最后他才把这个调研结果告诉你。 Cacheable 的 AOP 实现原理
Cacheable 的背后是一个功能极其强大的环绕通知 (Around)由 Spring 的 CacheInterceptor 实现。 代理和拦截和事务一样当 Spring 发现一个 Bean 的方法上有 Cacheable 注解时会为它创建一个 AOP 代理对象。所有对该方法的调用都会先被这个代理对象拦截。 CacheInterceptor (环绕通知) 开始工作 这个拦截器在调用真正的目标方法之前会执行以下逻辑 a. 生成缓存键 (Cache Key) * 拦截器会解析 Cacheable 注解。 * 它使用 value 属性 (如 products) 作为缓存的命名空间或称为缓存区域。 * 它使用 key 属性 (如 #id) 中的 SpEL (Spring Expression Language) 表达式来根据方法参数动态生成一个唯一的键。例如当调用 findProductById(123L) 时#id 会被替换为 123L最终生成的键可能是 products::123。 b. 查询缓存 * 拦截器拿着这个生成的键去配置好的缓存管理器CacheManager其背后可能是 Redis, Caffeine, EhCache 等中进行查询。 决策点缓存是否命中 这是环绕通知威力最大的体现因为它可以控制目标方法是否执行。 情况一缓存命中 (Cache Hit) 拦截器在缓存中找到了对应的值。它会立即将这个值返回给调用方。最关键的一步目标方法 findProductById 根本不会被执行 这就是所谓的“方法短路 (Short-Circuiting)”。它避免了数据库查询或远程调用。 情况二缓存未命中 (Cache Miss) 拦截器在缓存中没有找到任何东西。它会继续执行调用链即调用 pjp.proceed() 来执行原始的目标方法 findProductById。当目标方法成功执行并返回一个结果后拦截器会捕获这个返回值。它会将这个返回值以之前生成的缓存键存入缓存中以备下次使用。最后将这个返回值交还给调用方。
其他缓存注解的 AOP 原理 CachePut: 这个注解同样使用 AOP 拦截。但它的逻辑是总会执行目标方法比如更新数据库的操作。在方法执行成功后它会总是将方法的返回值更新到缓存中。它用于确保在数据更新后缓存中的数据也是最新的。它没有“短路”行为。 CacheEvict: 这个注解也使用 AOP 拦截。它的逻辑通常是在目标方法成功执行后默认根据 key 去缓存中删除一个或多个条目。它用于在删除或修改数据后清除相关的旧缓存确保数据一致性。
总结
注解AOP 实现方式 (简化理解)是否执行目标方法CacheableAround查缓存 - (命中) 短路返回 / (未命中) 执行并存入缓存不一定CachePutAround执行方法 - 将结果更新到缓存总是执行CacheEvictAfterReturning (默认)执行方法 - 从缓存中删除条目总是执行
所以Spring 的声明式缓存是 AOP 的实践之一。它通过 AOP 将复杂的、与业务无关的缓存管理逻辑完全分离让开发者只需关注业务本身极大地提高了代码的可读性和可维护性。