从graphql-java-kickstart迁移至Spring GraphQL时,如何重构DataLoader依赖配置?
迁移Spring GraphQL时DataLoader跨依赖问题解决
问题场景
从graphql-java-kickstart迁移至Spring GraphQL,原项目中存在一个DataLoader作为另一个DataLoader方法参数的场景(核心逻辑需保留,后续再解耦重构)。原kickstart配置可直接传递已定义的DataLoader实例,但Spring GraphQL使用BatchLoaderRegistry配置时,无法直接从Lambda中获取其他已注册的DataLoader。
原kickstart核心代码
@Component public class RequestContextBuilder extends DefaultGraphQLServletContextBuilder { @Override public GraphQLKickstartContext build(HttpServletRequest request, HttpServletResponse response) { Map<Object, Object> map = new HashMap<>(); // 其他上下文初始化逻辑 return GraphQLKickstartContext.of(buildDataLoaderRegistry(), map); } private DataLoaderRegistry buildDataLoaderRegistry() { var noBatching = new DataLoaderOptions().setBatchingEnabled(false); var dataLoaderRegistry = new DataLoaderRegistry(); // 注册第一个DataLoader var firstLoader = DataLoaderFactory.newDataLoader( ids1 -> supplyAsync(() -> firstService.getItems(ids1), traceableExecutorService), noBatching ); dataLoaderRegistry.register("first", firstLoader); // 注册第二个DataLoader,并直接传入firstLoader实例 var secondLoader = DataLoaderFactory.newDataLoader( ids2 -> supplyAsync(() -> secondService.loadUsingOtherLoader(ids2, firstLoader), traceableExecutorService), noBatching ); dataLoaderRegistry.register("second", secondLoader); return dataLoaderRegistry; } } @Service public class SecondService { public List<Item> loadUsingOtherLoader(List<ID> ids, DataLoader<ID, Item> firstLoader) { return ids.stream() .map(firstLoader::load) // 业务处理逻辑 .toList(); } }
Spring GraphQL当前配置问题
@Configuration public class RequestContextBuilder { public RequestContextBuilder(BatchLoaderRegistry registry, FirstService firstService, SecondService secondService) { registry.forName("first") .withOptions(BATCHING_DISABLED) .registerBatchLoader((ids, env) -> Flux.fromStream(ids.stream()).map(firstService::getItems)); // 无法直接获取firstLoader传递给secondService registry.forName("second") .withOptions(BATCHING_DISABLED) .registerBatchLoader((ids, env) -> Flux.fromStream(ids.stream()).map(secondService::loadUsingOtherLoader)); } }
解决方案
利用Spring GraphQL提供的BatchLoaderEnvironment中的DataLoaderAccessor,可以获取已注册的DataLoader实例,无需大幅修改原有业务代码。
方案1:修改SecondService参数为DataLoaderAccessor
@Service public class SecondService { public List<Item> loadUsingOtherLoader(List<ID> ids, DataLoaderAccessor dataLoaderAccessor) { // 通过名称获取已注册的firstLoader DataLoader<ID, Item> firstLoader = dataLoaderAccessor.getDataLoader("first"); return ids.stream() .map(firstLoader::load) // 业务处理逻辑 .toList(); } }
然后在配置类中从环境取出DataLoaderAccessor传递给方法:
@Configuration public class DataLoaderConfig { public DataLoaderConfig(BatchLoaderRegistry registry, FirstService firstService, SecondService secondService) { registry.forName("first") .withOptions(BATCHING_DISABLED) .registerBatchLoader((ids, env) -> Flux.fromIterable(firstService.getItems(ids))); registry.forName("second") .withOptions(BATCHING_DISABLED) .registerBatchLoader((ids, env) -> { // 从环境中获取DataLoaderAccessor DataLoaderAccessor accessor = env.getDataLoaderAccessor(); return Flux.fromIterable(secondService.loadUsingOtherLoader(ids, accessor)); }); } }
方案2:不修改SecondService,直接在配置中传递DataLoader
如果不想改动SecondService的方法签名,可以在BatchLoader的Lambda中先获取firstLoader,再传入方法:
@Configuration public class DataLoaderConfig { public DataLoaderConfig(BatchLoaderRegistry registry, FirstService firstService, SecondService secondService) { registry.forName("first") .withOptions(BATCHING_DISABLED) .registerBatchLoader((ids, env) -> Flux.fromIterable(firstService.getItems(ids))); registry.forName("second") .withOptions(BATCHING_DISABLED) .registerBatchLoader((ids, env) -> { // 直接获取firstLoader实例 DataLoader<ID, Item> firstLoader = env.getDataLoaderAccessor().getDataLoader("first"); return Flux.fromIterable(secondService.loadUsingOtherLoader(ids, firstLoader)); }); } }
两种方案都能在保留原有业务逻辑的前提下,实现Spring GraphQL中DataLoader的跨依赖调用,满足迁移初期的需求。
内容的提问来源于stack exchange,提问作者Michiel
相关产品推荐
相关产品推荐

