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

Java Spring Boot单例服务中实现单请求内函数缓存

单请求内缓存方法调用结果的解决方案

针对你在Spring Boot项目中需要仅在单个请求内缓存getValue方法结果的需求,以下是几种可行的实现方案:

方案一:使用@RequestScope的缓存容器

利用Spring的@RequestScope创建请求级别的缓存Map,注入到单例的LogicService中(通过代理模式实现),每个请求都会拥有独立的缓存实例,跨请求不会共享数据。

步骤:

  1. 创建请求作用域的缓存Bean:
@Component
@RequestScope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestScopedCache {
    private final Map<String, String> cache = new ConcurrentHashMap<>();

    public String get(String key) {
        return cache.get(key);
    }

    public void put(String key, String value) {
        cache.put(key, value);
    }

    public boolean containsKey(String key) {
        return cache.containsKey(key);
    }
}
  1. 修改LogicService,注入缓存Bean并实现手动缓存逻辑:
@Service
@RequiredArgsConstructor
@Slf4j
public class LogicService {
    private final RequestScopedCache requestScopedCache;

    public Map<String, String> getValues() {
        Map<String, String> result = new HashMap<>();
        // 示例:处理一批可能包含重复的key
        List<String> keys = Arrays.asList("key1", "key2", "key1", "key3");
        for (String key : keys) {
            result.put(key, getValue(key));
        }
        return result;
    }

    public String getValue(String key) {
        // 优先读取请求级缓存
        if (requestScopedCache.containsKey(key)) {
            log.debug("Hit request cache for key: {}", key);
            return requestScopedCache.get(key);
        }
        // 调用外部服务的耗时逻辑
        String value = fetchFromExternalService(key);
        // 存入请求级缓存
        requestScopedCache.put(key, value);
        return value;
    }

    // 模拟外部服务调用
    private String fetchFromExternalService(String key) {
        log.debug("Fetching value for key: {}", key);
        return "value_" + key;
    }
}

此方案由Spring自动管理缓存生命周期,请求结束后缓存实例会被销毁,无需手动清理。

方案二:使用ThreadLocal存储缓存

默认Servlet容器(如Tomcat)为每个请求分配独立线程,可通过ThreadLocal存储当前请求的缓存Map,注意请求结束后必须清理ThreadLocal以避免内存泄漏。

步骤:

  1. 在LogicService中定义ThreadLocal缓存:
@Service
@RequiredArgsConstructor
@Slf4j
public class LogicService {
    private final ThreadLocal<Map<String, String>> requestCache = ThreadLocal.withInitial(HashMap::new);

    public Map<String, String> getValues() {
        try {
            Map<String, String> result = new HashMap<>();
            List<String> keys = Arrays.asList("key1", "key2", "key1", "key3");
            for (String key : keys) {
                result.put(key, getValue(key));
            }
            return result;
        } finally {
            // 请求结束后强制清理ThreadLocal
            requestCache.remove();
        }
    }

    public String getValue(String key) {
        Map<String, String> cache = requestCache.get();
        if (cache.containsKey(key)) {
            log.debug("Hit thread-local cache for key: {}", key);
            return cache.get(key);
        }
        String value = fetchFromExternalService(key);
        cache.put(key, value);
        return value;
    }

    private String fetchFromExternalService(String key) {
        log.debug("Fetching value for key: {}", key);
        return "value_" + key;
    }

    // 供拦截器调用的清理方法
    public void clearRequestCache() {
        requestCache.remove();
    }
}
  1. 配置请求拦截器统一清理(可选,避免手动清理遗漏):
@Component
public class RequestCacheCleanupInterceptor implements HandlerInterceptor {
    @Autowired
    private LogicService logicService;

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        logicService.clearRequestCache();
    }
}
  1. 注册拦截器:
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private RequestCacheCleanupInterceptor requestCacheCleanupInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(requestCacheCleanupInterceptor).addPathPatterns("/logic");
    }
}

方案三:局部缓存传递(轻量直接方案)

在getValues方法内创建局部缓存Map,作为参数传递给getValue,完全规避单例Bean的状态共享问题。

修改LogicService:

@Service
@RequiredArgsConstructor
@Slf4j
public class LogicService {

    public Map<String, String> getValues() {
        // 局部缓存仅在当前方法调用(单个请求)内有效
        Map<String, String> localCache = new HashMap<>();
        Map<String, String> result = new HashMap<>();
        List<String> keys = Arrays.asList("key1", "key2", "key1", "key3");
        for (String key : keys) {
            result.put(key, getValue(key, localCache));
        }
        return result;
    }

    public String getValue(String key, Map<String, String> localCache) {
        if (localCache.containsKey(key)) {
            log.debug("Hit local cache for key: {}", key);
            return localCache.get(key);
        }
        String value = fetchFromExternalService(key);
        localCache.put(key, value);
        return value;
    }

    private String fetchFromExternalService(String key) {
        log.debug("Fetching value for key: {}", key);
        return "value_" + key;
    }
}

此方案无需依赖Spring作用域或ThreadLocal,逻辑简单直接,适合场景单一的需求。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 19:40:27