玩转Spring Cache --- @Cacheable/@CachePut/@CacheEvict缓存注解相关基础类打点【享学Spring】(下)

简介: 玩转Spring Cache --- @Cacheable/@CachePut/@CacheEvict缓存注解相关基础类打点【享学Spring】(下)

AbstractCacheResolver

具体实现根据调用上下文提供缓存名称集合。


// @since 4.1  实现了InitializingBean
public abstract class AbstractCacheResolver implements CacheResolver, InitializingBean {
  // 课件它还是依赖于CacheManager的
  @Nullable
  private CacheManager cacheManager;
  // 构造函数统一protected
  protected AbstractCacheResolver() {
  }
  protected AbstractCacheResolver(CacheManager cacheManager) {
    this.cacheManager = cacheManager;
  }
  // 设置,指定一个CacheManager
  public void setCacheManager(CacheManager cacheManager) {
    this.cacheManager = cacheManager;
  }
  public CacheManager getCacheManager() {
    Assert.state(this.cacheManager != null, "No CacheManager set");
    return this.cacheManager;
  }
  // 做了一步校验而已~~~CacheManager 必须存在
  // 这是一个使用技巧哦   自己的在设计框架的框架的时候可以使用~
  @Override
  public void afterPropertiesSet()  {
    Assert.notNull(this.cacheManager, "CacheManager is required");
  }
  @Override
  public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
    // getCacheNames抽象方法,子类去实现~~~~
    Collection<String> cacheNames = getCacheNames(context);
    if (cacheNames == null) {
      return Collections.emptyList();
    }
    // 根据cacheNames  去CacheManager里面拿到Cache对象, 作为最终的返回
    Collection<Cache> result = new ArrayList<>(cacheNames.size());
    for (String cacheName : cacheNames) {
      Cache cache = getCacheManager().getCache(cacheName);
      if (cache == null) {
        throw new IllegalArgumentException("Cannot find cache named '" +
            cacheName + "' for " + context.getOperation());
      }
      result.add(cache);
    }
    return result;
  }
  // 子类需要实现此抽象方法
  @Nullable
  protected abstract Collection<String> getCacheNames(CacheOperationInvocationContext<?> context);
}


此抽象类一样的,只是模版实现了resolveCaches()方法。抽象方法的实现交给了实现类

SimpleCacheResolver


public class SimpleCacheResolver extends AbstractCacheResolver {
  ...
  @Override
  protected Collection<String> getCacheNames(CacheOperationInvocationContext<?> context) {
    return context.getOperation().getCacheNames();
  }
  ... 
}


这个实现太简单了,没啥好说的。

NamedCacheResolver

public class NamedCacheResolver extends AbstractCacheResolver {
  @Nullable
  private Collection<String> cacheNames;
  public NamedCacheResolver() {
  }
  public NamedCacheResolver(CacheManager cacheManager, String... cacheNames) {
    super(cacheManager);
    this.cacheNames = new ArrayList<>(Arrays.asList(cacheNames));
  }
  /**
   * Set the cache name(s) that this resolver should use.
   */
  public void setCacheNames(Collection<String> cacheNames) {
    this.cacheNames = cacheNames;
  }
  @Override
  protected Collection<String> getCacheNames(CacheOperationInvocationContext<?> context) {
    return this.cacheNames;
  }
}



此解析器的特点是,可以自己setCacheNames()。


若内建的不符合条件,我们可以自己实现一个自己的CacheResolver。比如实现和业务相关的缓存处理器(若Class==某Class,做些特殊的操作之类的)


需要注意的是:即使你配置使用的是CacheResolver,你也必须在配置里提供cacheNames至少一个的,因为毕竟是根据你配置的CacheNames去CacheManager里找(当然,若是你的自定义实现除外)


CacheOperationSourcePointcut


Pointcut小伙伴应该不陌生,在AOP章节重点又重点的描述过,我们知道Pointcut直接关乎到是否要生成代理对象,所以此类还是蛮重要的。

// @since 3.1 它是个StaticMethodMatcherPointcut  所以方法入参它不关心
abstract class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
  // 如果你这个类就是一个CacheManager,不切入
  @Override
  public boolean matches(Method method, Class<?> targetClass) {
    if (CacheManager.class.isAssignableFrom(targetClass)) {
      return false;
    }
    // 获取到当前的缓存属性源~~~getCacheOperationSource()是个抽象方法
    CacheOperationSource cas = getCacheOperationSource();
    // 下面一句话解释为:如果方法/类上标注有缓存相关的注解,就切入进取~~
    // 具体逻辑请参见方法:cas.getCacheOperations();
    return (cas != null && !CollectionUtils.isEmpty(cas.getCacheOperations(method, targetClass)));
  }
  @Override
  public boolean equals(Object other) {
    if (this == other) {
      return true;
    }
    if (!(other instanceof CacheOperationSourcePointcut)) {
      return false;
    }
    CacheOperationSourcePointcut otherPc = (CacheOperationSourcePointcut) other;
    return ObjectUtils.nullSafeEquals(getCacheOperationSource(), otherPc.getCacheOperationSource());
  }
  @Override
  public int hashCode() {
    return CacheOperationSourcePointcut.class.hashCode();
  }
  @Override
  public String toString() {
    return getClass().getName() + ": " + getCacheOperationSource();
  }
  /**
   * Obtain the underlying {@link CacheOperationSource} (may be {@code null}).
   * To be implemented by subclasses.
   */
  @Nullable
  protected abstract CacheOperationSource getCacheOperationSource();
}


CacheErrorHandler


处理缓存发生错误时的策略接口。在大多数情况下,提供者抛出的任何异常都应该简单地被抛出到客户机上,但是在某些情况下,基础结构可能需要以不同的方式处理缓存提供者异常。这个时候可以使用此接口来处理

接口内容十分之简单:

public interface CacheErrorHandler {
  void handleCacheGetError(RuntimeException exception, Cache cache, Object key);
  void handleCachePutError(RuntimeException exception, Cache cache, Object key, @Nullable Object value);
  void handleCacheEvictError(RuntimeException exception, Cache cache, Object key);
  void handleCacheClearError(RuntimeException exception, Cache cache);  
}


pring对它唯一内建实现为SimpleCacheErrorHandler,代码我不贴了,全是空实现,所以它是一个Adapter适配器形式的存在。

若你想自己提供CacheErrorHandler,你可以继承自SimpleCacheErrorHandler来弄~~~


AbstractCacheInvoker

它的作用是在进行缓存操作时发生异常时,调用组件CacheErrorHandler来进行处理~

// @since 4.1
public abstract class AbstractCacheInvoker {
  //protected属性~
  protected SingletonSupplier<CacheErrorHandler> errorHandler;
  protected AbstractCacheInvoker() {
    this.errorHandler = SingletonSupplier.of(SimpleCacheErrorHandler::new);
  }
  protected AbstractCacheInvoker(CacheErrorHandler errorHandler) {
    this.errorHandler = SingletonSupplier.of(errorHandler);
  }
  // 此处用的obtain方法  它能保证不为null
  public CacheErrorHandler getErrorHandler() {
    return this.errorHandler.obtain();
  }
  @Nullable
  protected Cache.ValueWrapper doGet(Cache cache, Object key) {
    try {
      return cache.get(key);
    } catch (RuntimeException ex) {
      getErrorHandler().handleCacheGetError(ex, cache, key);
      // 只有它有返回值哦  返回null
      return null;  // If the exception is handled, return a cache miss
    }
  }
  ... // 省略doPut、doEvict、doClear  处理方式同上
}



可见这个类在Spring4.1提出,专门用于处理异常的,毕竟CacheErrorHandler也是Spring4.1后才有。


总结


本篇文章为讲解缓存注解的深入原理分析进行铺垫,所以密切关注这篇文章:

【小家Spring】玩转Spring Cache — @Cacheable/@CachePut/@CacheEvict注解的使用以及原理深度剖析

相关文章
|
5天前
|
运维 Java 程序员
Spring5深入浅出篇:基于注解实现的AOP
# Spring5 AOP 深入理解:注解实现 本文介绍了基于注解的AOP编程步骤,包括原始对象、额外功能、切点和组装切面。步骤1-3旨在构建切面,与传统AOP相似。示例代码展示了如何使用`@Around`定义切面和执行逻辑。配置中,通过`@Aspect`和`@Around`注解定义切点,并在Spring配置中启用AOP自动代理。 进一步讨论了切点复用,避免重复代码以提高代码维护性。通过`@Pointcut`定义通用切点表达式,然后在多个通知中引用。此外,解释了AOP底层实现的两种动态代理方式:JDK动态代理和Cglib字节码增强,默认使用JDK,可通过配置切换到Cglib
|
1天前
|
JSON 前端开发 Java
【JAVA进阶篇教学】第七篇:Spring中常用注解
【JAVA进阶篇教学】第七篇:Spring中常用注解
|
4天前
|
JavaScript Java 开发者
Spring Boot中的@Lazy注解:概念及实战应用
【4月更文挑战第7天】在Spring Framework中,@Lazy注解是一个非常有用的特性,它允许开发者控制Spring容器的bean初始化时机。本文将详细介绍@Lazy注解的概念,并通过一个实际的例子展示如何在Spring Boot应用中使用它。
16 2
|
5天前
|
前端开发 Java
SpringBoot之自定义注解参数校验
SpringBoot之自定义注解参数校验
15 2
|
5天前
|
存储 消息中间件 缓存
Redis缓存技术详解
【5月更文挑战第6天】Redis是一款高性能内存数据结构存储系统,常用于缓存、消息队列、分布式锁等场景。其特点包括速度快(全内存存储)、丰富数据类型、持久化、发布/订阅、主从复制和分布式锁。优化策略包括选择合适数据类型、设置过期时间、使用Pipeline、开启持久化、监控调优及使用集群。通过这些手段,Redis能为系统提供高效稳定的服务。
|
11天前
|
存储 缓存 NoSQL
【Go语言专栏】Go语言中的Redis操作与缓存应用
【4月更文挑战第30天】本文探讨了在Go语言中使用Redis进行操作和缓存应用的方法。文章介绍了Redis作为高性能键值存储系统,用于提升应用性能。推荐使用`go-redis/redis`库,示例代码展示了连接、设置、获取和删除键值对的基本操作。文章还详细阐述了缓存应用的步骤及常见缓存策略,包括缓存穿透、缓存击穿和缓存雪崩的解决方案。利用Redis和合适策略可有效优化应用性能。
|
14天前
|
存储 缓存 NoSQL
Redis多级缓存指南:从前端到后端全方位优化!
本文探讨了现代互联网应用中,多级缓存的重要性,特别是Redis在缓存中间件的角色。多级缓存能提升数据访问速度、系统稳定性和可扩展性,减少数据库压力,并允许灵活的缓存策略。浏览器本地内存缓存和磁盘缓存分别优化了短期数据和静态资源的存储,而服务端本地内存缓存和网络内存缓存(如Redis)则提供了高速访问和分布式系统的解决方案。服务器本地磁盘缓存因I/O性能瓶颈和复杂管理而不推荐用于缓存,强调了内存和网络缓存的优越性。
40 1
|
1天前
|
缓存 NoSQL 应用服务中间件
Redis多级缓存
Redis多级缓存
7 0
|
1天前
|
缓存 NoSQL 关系型数据库
Redis 缓存 一致性
Redis 缓存 一致性
5 0
|
1天前
|
缓存 监控 NoSQL
Redis经典问题:缓存穿透
本文介绍了缓存穿透问题在分布式系统和缓存应用中的严重性,当请求的数据在缓存和数据库都不存在时,可能导致数据库崩溃。为解决此问题,提出了五种策略:接口层增加校验、缓存空值、使用布隆过滤器、数据库查询优化和加强监控报警机制。通过这些方法,可以有效缓解缓存穿透对系统稳定性的影响。
47 3