玩转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注解的使用以及原理深度剖析

相关文章
|
缓存 Java 应用服务中间件
Spring Boot配置优化:Tomcat+数据库+缓存+日志,全场景教程
本文详解Spring Boot十大核心配置优化技巧,涵盖Tomcat连接池、数据库连接池、Jackson时区、日志管理、缓存策略、异步线程池等关键配置,结合代码示例与通俗解释,助你轻松掌握高并发场景下的性能调优方法,适用于实际项目落地。
2124 5
|
缓存 监控 Linux
Linux系统清理缓存(buff/cache)的有效方法。
总结而言,在大多数情形下你不必担心Linux中buffer与cache占用过多内存在影响到其他程序运行;因为当程序请求更多内存在没有足够可用资源时,Linux会自行调整其占有量。只有当你明确知道当前环境与需求并希望立即回收这部分资源给即将运行重负载任务之前才考虑上述方法去主动干预。
2823 10
|
存储 缓存 Java
Spring中@Cacheable、@CacheEvict以及其他缓存相关注解的实用介绍
缓存是提升应用性能的重要技术,Spring框架提供了丰富的缓存注解,如`@Cacheable`、`@CacheEvict`等,帮助开发者简化缓存管理。本文介绍了如何在Spring中配置缓存管理器,使用缓存注解优化数据访问,并探讨了缓存的最佳实践,以提升系统响应速度与可扩展性。
523 0
Spring中@Cacheable、@CacheEvict以及其他缓存相关注解的实用介绍
|
缓存 NoSQL Java
什么是缓存?如何在 Spring Boot 中使用缓存框架
什么是缓存?如何在 Spring Boot 中使用缓存框架
1205 0
|
缓存 Java 数据库
SpringBoot缓存注解使用
Spring Boot 提供了一套方便的缓存注解,用于简化缓存管理。通过 `@Cacheable`、`@CachePut`、`@CacheEvict` 和 `@Caching` 等注解,开发者可以轻松地实现方法级别的缓存操作,从而提升应用的性能和响应速度。合理使用这些注解可以大大减少数据库的访问频率,优化系统性能。
1034 89
|
消息中间件 缓存 NoSQL
基于Spring Data Redis与RabbitMQ实现字符串缓存和计数功能(数据同步)
总的来说,借助Spring Data Redis和RabbitMQ,我们可以轻松实现字符串缓存和计数的功能。而关键的部分不过是一些"厨房的套路",一旦你掌握了这些套路,那么你就像厨师一样可以准备出一道道饕餮美食了。通过这种方式促进数据处理效率无疑将大大提高我们的生产力。
502 32
|
缓存 Java 数据库连接
怎么使用注解开启二级缓存,注解应该放在那里?
在 MyBatis 中,使用 `@CacheNamespace` 注解可开启二级缓存,该注解应添加在 Mapper 接口上。通过配置 `eviction`、`flushInterval`、`size` 等参数,可以控制缓存行为。此外,实体类需实现 `Serializable` 接口以确保缓存正常工作。
368 1
|
Java Spring
【Spring】方法注解@Bean,配置类扫描路径
@Bean方法注解,如何在同一个类下面定义多个Bean对象,配置扫描路径
851 73
|
存储 缓存 NoSQL
Spring Cache缓存框架
Spring Cache是Spring体系下的标准化缓存框架,支持多种缓存(如Redis、EhCache、Caffeine),可独立或组合使用。其优势包括平滑迁移、注解与编程两种使用方式,以及高度解耦和灵活管理。通过动态代理实现缓存操作,适用于不同业务场景。
906 0
|
前端开发 IDE Java
Spring MVC 中因导入错误的 Model 类报错问题解析
在 Spring MVC 或 Spring Boot 开发中,若导入错误的 `Model` 类(如 `ch.qos.logback.core.model.Model`),会导致无法解析 `addAttribute` 方法的错误。正确类应为 `org.springframework.ui.Model`。此问题通常因 IDE 自动导入错误类引起。解决方法包括:删除错误导入、添加正确包路径、验证依赖及清理缓存。确保代码中正确使用 Spring 提供的 `Model` 接口以实现前后端数据传递。
679 0