Spring缓存只绑定到当前事务

Spring cache binded only to the current transaction

我正试图说服我的公司使用 spring 3.2 的缓存(我知道它很旧)。

该应用程序构建于 top alfresco 5.x(构建于 spring 3.2 之上)。

目前,我们有一些缓存绑定到当前事务:

if (AlfrescoTransactionSupport.getTransactionReadState() == TxnReadState.TXN_READ_ONLY) {
  cache = (Map<String, Boolean>) AlfrescoTransactionSupport.getResource(CACHED_NAME);
  if (cache == null) {
    cache = new HashMap<String, Boolean>();
  }
  AlfrescoTransactionSupport.bindResource(CACHED_NAME, cache);
}

缓存仅对当前读取事务有效,然后销毁。

我试过了

@Cacheable("cache_name") 
@Transactional(readOnly=true)

注解,但是打开读写事务时,缓存并没有被销毁。

知道如何以 spring 方式做到这一点吗?

@biiyamn 是对的,

我必须实现自己的缓存才能做到这一点。

首先,我实现了 BeanFactory:

import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.StringUtils;

public class KReadTransactionCacheFactoryBean implements FactoryBean<KReadTransactionCache>, BeanNameAware,
    InitializingBean {

  private String name = "";

  private boolean allowNullValues = true;

  private KReadTransactionCache cache;

  /**
   * Specify the name of the cache.
   * <p>Default is "" (empty String).
   */
  public void setName(String name) {
    this.name = name;
  }

  /**
   * Set whether to allow {@code null} values
   * (adapting them to an internal null holder value).
   * <p>Default is "true".
   */
  public void setAllowNullValues(boolean allowNullValues) {
    this.allowNullValues = allowNullValues;
  }

  public void setBeanName(String beanName) {
    if (!StringUtils.hasLength(this.name)) {
      setName(beanName);
    }
  }

  public void afterPropertiesSet() {
    this.cache = new KReadTransactionCache(this.name, this.allowNullValues);
  }

  public KReadTransactionCache getObject() {
    return this.cache;
  }

  public Class<?> getObjectType() {
    return KReadTransactionCache.class;
  }

  public boolean isSingleton() {
    return false;
  }

}

然后,实现de缓存绑定到当前事务

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;

public class KReadTransactionCache implements Cache {

  private static final Object NULL_HOLDER = new NullHolder();

  private final String name;

  private final boolean allowNullValues;

  /**
   * Create a new ConcurrentMapCache with the specified name.
   * @param name the name of the cache
   */
  public KReadTransactionCache(String name) {
    this(name, true);
  }

  protected static Map<Object, Object> getBindedCache(String name) {
    Map<Object, Object> cache = null;
    if (AlfrescoTransactionSupport.getTransactionReadState() == TxnReadState.TXN_READ_ONLY) {
      cache = AlfrescoTransactionSupport.getResource(name);
      if (cache == null) {
        cache = new HashMap<>();
      }
      AlfrescoTransactionSupport.bindResource(name, cache);
    }
    return cache;
  }

  /**
   * Create a new Map with the specified name and the
   * given internal ConcurrentMap to use.
   * @param name the name of the cache
   * @param allowNullValues whether to allow {@code null} values
   * (adapting them to an internal null holder value)
   */
  public KReadTransactionCache(String name, boolean allowNullValues) {
    this.name = name;
    this.allowNullValues = allowNullValues;

  }

  public String getName() {
    return this.name;
  }

  public Map getNativeCache() {
    return getBindedCache(name);
  }

  public boolean isAllowNullValues() {
    return this.allowNullValues;
  }

  public ValueWrapper get(Object key) {
    final Map<Object, Object> bindedCache = getBindedCache(name);
    if (bindedCache == null) {
      return null;
    }
    Object value = bindedCache.get(key);
    return (value != null ? new SimpleValueWrapper(fromStoreValue(value)) : null);
  }

  public void put(Object key, Object value) {
    final Map<Object, Object> bindedCache = getBindedCache(name);
    if (bindedCache == null) {
      return;
    }

    bindedCache.put(key, toStoreValue(value));
  }

  public void evict(Object key) {
    final Map<Object, Object> bindedCache = getBindedCache(name);
    if (bindedCache == null) {
      return;
    }
    bindedCache.remove(key);
  }

  public void clear() {
    final Map<Object, Object> bindedCache = getBindedCache(name);
    if (bindedCache == null) {
      return;
    }
    bindedCache.clear();
  }

  /**
   * Convert the given value from the internal store to a user value
   * returned from the get method (adapting {@code null}).
   * @param storeValue the store value
   * @return the value to return to the user
   */
  protected Object fromStoreValue(Object storeValue) {
    if (this.allowNullValues && storeValue == NULL_HOLDER) {
      return null;
    }
    return storeValue;
  }

  /**
   * Convert the given user value, as passed into the put method,
   * to a value in the internal store (adapting {@code null}).
   * @param userValue the given user value
   * @return the value to store
   */
  protected Object toStoreValue(Object userValue) {
    if (this.allowNullValues && userValue == null) {
      return NULL_HOLDER;
    }
    return userValue;
  }

  @SuppressWarnings("serial")
  private static class NullHolder implements Serializable {
  }

}

和 xml 配置:

    <!-- *******************************
       ***** CACHE CONFIGURATION *****
       ******************************* -->
   <!-- simple cache manager -->
   <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
      <property name="caches">
         <set>
            <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="default" />
            <bean class="path.to.package.KReadTransactionCacheFactoryBean" p:name="cacheNameByAnnotation" />
            <!-- TODO Add other cache instances in here -->
         </set>
      </property>
   </bean>
  1. SimpleCacheManager 可用于测试环境,如 spring doc
  2. SimpleCacheManager 只支持在配置时预定义缓存的静态模式,不允许在运行时添加缓存
  3. EhCache 及其相关桥接器 spring EhCacheManager 可能是一个不错的选择