Spring请求作用域下如何避免重复发起网络调用?
我有一个Spring请求作用域(Request Scoped)的组件,用于发起网络调用获取数据:
@Component @RequestScope public class NetworkDataProvider { @Inject private ServiceClient serviceClient; public Optional<List<String>> getSomeData() { return serviceClient.getSomeData(); // 发起网络请求获取数据 } }
我希望在多个其他组件中使用这些数据:
@Component public class Component1 { @Inject private NetworkDataProvider networkDataProvider; public String getSomething() { Optional<List<String>> data = networkDataProvider.getSomeData(); // 业务逻辑处理 } }
@Component public class Component2 { @Inject private NetworkDataProvider networkDataProvider; public String getSomething() { Optional<List<String>> data = networkDataProvider.getSomeData(); // 业务逻辑处理 } }
这些组件被用于控制器中:
@Controller public class MyController { @Inject private Component1 component1; @Inject private Component2 component2; public ModelAndView mv() { String string1 = component1.getSomething(); String string2 = component2.getSomething(); // 组装视图 } }
目前由于ServiceClient会被两个不同的组件调用两次,因此会发起两次网络请求。请问在同一次请求中,避免多次发起网络调用并复用数据的最佳方式是什么?
方式一:在请求作用域组件内部缓存数据
既然NetworkDataProvider本身就是请求作用域的,直接在组件内部缓存获取到的数据,确保同一次请求内只调用一次网络接口:
@Component @RequestScope public class NetworkDataProvider { @Inject private ServiceClient serviceClient; private Optional<List<String>> cachedData; public Optional<List<String>> getSomeData() { if (cachedData == null) { cachedData = serviceClient.getSomeData(); } return cachedData; } }
这种方式最简单直接,利用请求作用域组件的生命周期(同一次请求内实例唯一),第一次调用时发起网络请求并缓存结果,后续调用直接返回缓存值。
方式二:在控制器层提前获取数据并传递
如果不想修改NetworkDataProvider,可以在控制器中先获取数据,再传递给Component1和Component2:
@Controller public class MyController { @Inject private Component1 component1; @Inject private Component2 component2; @Inject private NetworkDataProvider networkDataProvider; public ModelAndView mv() { Optional<List<String>> sharedData = networkDataProvider.getSomeData(); String string1 = component1.getSomething(sharedData); String string2 = component2.getSomething(sharedData); // 组装视图 } }
同时修改Component1和Component2的方法,接收外部传入的数据:
@Component public class Component1 { public String getSomething(Optional<List<String>> data) { // 业务逻辑处理 } }
这种方式适合需要明确控制数据流向的场景,避免组件之间隐式依赖请求作用域的缓存。
方式三:结合Spring缓存使用请求作用域缓存
如果项目中已经使用Spring Cache,可以配置一个请求作用域的缓存管理器,或者给getSomeData方法添加@Cacheable注解并指定缓存的作用域为请求级别:
@Component @RequestScope public class NetworkDataProvider { @Inject private ServiceClient serviceClient; @Cacheable(cacheNames = "requestScopedCache", key = "#root.methodName") public Optional<List<String>> getSomeData() { return serviceClient.getSomeData(); } }
需要确保缓存配置是请求作用域的(比如使用SimpleCacheManager结合ConcurrentMapCache,并将缓存Bean设置为@RequestScope),这样同一次请求内的多次调用会复用缓存结果,请求结束后缓存自动失效。
推荐优先使用方式一,因为它最贴合现有代码结构,改动最小,且利用了请求作用域组件的天然特性,无需额外配置或调整数据流向。
内容的提问来源于stack exchange,提问作者Chirag Arora

