You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Alfresco 5.x的Spring 3.2事务绑定缓存技术咨询

Migrate Transaction-Bound Cache to Spring 3.2 (Alfresco 5.x)

Hey there! Let’s walk through adapting your existing transaction-bound cache to Spring 3.2’s caching abstraction, tailored to Alfresco 5.x’s dependency constraints. Your current code ties cache storage to read-only transactions via AlfrescoTransactionSupport—we can replicate this behavior using Spring’s native caching with a few custom components.

1. Understand the Core Requirement

Your existing logic only uses cache in read-only transactions, and the cache is scoped to the current transaction (cleaned up when the transaction ends). Spring 3.2’s default caches (like ConcurrentMapCache) are global, so we need to build a custom transaction-scoped cache that integrates with Alfresco’s transaction lifecycle.

2. Implement a Transaction-Bound Cache

First, create a custom Cache implementation that uses AlfrescoTransactionSupport to bind cache storage to the current read-only transaction:

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

import java.util.HashMap;
import java.util.Map;

public class TransactionBoundCache implements Cache {
    private final String cacheName;

    public TransactionBoundCache(String cacheName) {
        this.cacheName = cacheName;
    }

    @Override
    public String getName() {
        return cacheName;
    }

    @Override
    public Object getNativeCache() {
        Map<String, Object> cache = getTransactionCache();
        return cache != null ? cache : new HashMap<>();
    }

    @Override
    public ValueWrapper get(Object key) {
        Map<String, Object> cache = getTransactionCache();
        if (cache == null) return null;
        
        Object value = cache.get(key.toString());
        return value != null ? new SimpleValueWrapper(value) : null;
    }

    @Override
    public <T> T get(Object key, Class<T> type) {
        ValueWrapper wrapper = get(key);
        return wrapper != null ? type.cast(wrapper.get()) : null;
    }

    @Override
    public void put(Object key, Object value) {
        Map<String, Object> cache = getTransactionCache();
        if (cache != null) {
            cache.put(key.toString(), value);
        }
    }

    @Override
    public void evict(Object key) {
        Map<String, Object> cache = getTransactionCache();
        if (cache != null) {
            cache.remove(key.toString());
        }
    }

    @Override
    public void clear() {
        Map<String, Object> cache = getTransactionCache();
        if (cache != null) {
            cache.clear();
        }
    }

    // Helper to get/initialize transaction-scoped cache
    private Map<String, Object> getTransactionCache() {
        // Only enable cache for read-only transactions (matches your existing logic)
        if (AlfrescoTransactionSupport.getTransactionReadState() != TxnReadState.TXN_READ_ONLY) {
            return null;
        }

        @SuppressWarnings("unchecked")
        Map<String, Object> cache = (Map<String, Object>) AlfrescoTransactionSupport.getResource(cacheName);
        if (cache == null) {
            cache = new HashMap<>();
            AlfrescoTransactionSupport.bindResource(cacheName, cache);
        }
        return cache;
    }
}

3. Build a Custom Cache Manager

Next, create a CacheManager to manage your transaction-bound caches:

import org.springframework.cache.Cache;
import org.springframework.cache.support.AbstractCacheManager;

import java.util.HashMap;
import java.util.Map;

public class TransactionBoundCacheManager extends AbstractCacheManager {
    private final Map<String, Cache> cacheMap = new HashMap<>();

    @Override
    protected Collection<? extends Cache> loadCaches() {
        return cacheMap.values();
    }

    // Add caches programmatically or via Spring config
    public void addCache(String cacheName) {
        cacheMap.put(cacheName, new TransactionBoundCache(cacheName));
        afterPropertiesSet(); // Refresh the cache manager
    }
}

4. Configure Spring to Use Your Cache Setup

XML Configuration (Alfresco 5.x typically uses XML)

Add this to your Spring context file to enable caching and register your custom components:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/cache
           http://www.springframework.org/schema/cache/spring-cache.xsd">

    <!-- Enable annotation-driven caching -->
    <cache:annotation-driven cache-manager="transactionBoundCacheManager"/>

    <!-- Register custom cache manager -->
    <bean id="transactionBoundCacheManager" class="com.your.package.TransactionBoundCacheManager">
        <property name="cacheMap">
            <map>
                <!-- Replace "yourCachedName" with your original CACHED_NAME -->
                <entry key="yourCachedName" value-ref="yourCachedNameCache"/>
            </map>
        </property>
    </bean>

    <!-- Define your transaction-bound cache -->
    <bean id="yourCachedNameCache" class="com.your.package.TransactionBoundCache">
        <constructor-arg value="yourCachedName"/>
    </bean>
</beans>

Java Configuration (If your setup supports it)

If you use Java-based config, use this:

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public CacheManager transactionBoundCacheManager() {
        TransactionBoundCacheManager manager = new TransactionBoundCacheManager();
        manager.addCache("yourCachedName"); // Match your original CACHED_NAME
        return manager;
    }
}

5. Migrate Your Business Logic to Spring Cache Annotations

Replace your manual cache handling with Spring’s @Cacheable annotation. We’ll use a SpEL condition to restrict caching to read-only transactions, matching your original logic:

Original Code (Manual Cache)

public Boolean checkPermission(String key) {
    if (AlfrescoTransactionSupport.getTransactionReadState() == TxnReadState.TXN_READ_ONLY) {
        Map<String, Boolean> cache = (Map<String, Boolean>) AlfrescoTransactionSupport.getResource(CACHED_NAME);
        if (cache == null) {
            cache = new HashMap<>();
            AlfrescoTransactionSupport.bindResource(CACHED_NAME, cache);
        }
        Boolean value = cache.get(key);
        if (value == null) {
            value = computePermission(key); // Your core logic
            cache.put(key, value);
        }
        return value;
    } else {
        return computePermission(key);
    }
}

Migrated Code (Spring Cache)

import org.springframework.cache.annotation.Cacheable;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.TxnReadState;

// Cache only in read-only transactions
@Cacheable(
    value = "yourCachedName",
    condition = "T(org.alfresco.repo.transaction.AlfrescoTransactionSupport).getTransactionReadState() == T(org.alfresco.repo.transaction.TxnReadState).TXN_READ_ONLY"
)
public Boolean checkPermission(String key) {
    // Only your core logic remains—cache handling is delegated to Spring
    return computePermission(key);
}

6. Key Notes

  • Transaction Cleanup: Alfresco’s transaction manager automatically cleans up resources bound via AlfrescoTransactionSupport when the transaction ends, so your cache will be scoped to the current transaction (no leftover data between transactions).
  • Testing: Validate that caching works only in read-only transactions, and that cache entries are not retained after the transaction completes.
  • Compatibility: This setup is fully compatible with Spring 3.2 and Alfresco 5.x, as it leverages Alfresco’s existing transaction support alongside Spring’s cache abstraction.

内容的提问来源于stack exchange,提问作者Nymeria

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:37:24