如何在DGS+Spring Boot框架下不通过传参访问频繁实例化的对象
可行的线程安全实现方案
有两种成熟的实现方式可以满足需求,无需在方法签名中传递上下文参数,同时保证线程安全:
方案1:基于ThreadLocal实现上下文持有者
改造原有RequestContextHolder,使用ThreadLocal存储上下文,利用ThreadLocal的线程隔离特性保证线程安全,同时配合DGS请求拦截器完成上下文的设置和清理,避免内存泄漏。
改造后的RequestContextHolder代码
@Component public class RequestContextHolder { private static final ThreadLocal<RequestContext> CONTEXT_HOLDER = new ThreadLocal<>(); public void setRequestContext(RequestContext context) { CONTEXT_HOLDER.set(context); } public RequestContext getRequestContext() { return CONTEXT_HOLDER.get(); } public void clear() { CONTEXT_HOLDER.remove(); } }
新增DGS请求拦截器完成上下文绑定
实现DGS自带的DgsRequestInterceptor接口,在请求预处理阶段将自定义上下文存入ThreadLocal,请求结束后清理:
@Component public class ContextBindInterceptor implements DgsRequestInterceptor { private final RequestContextHolder requestContextHolder; public ContextBindInterceptor(RequestContextHolder requestContextHolder) { this.requestContextHolder = requestContextHolder; } @Override public void preProcess(DgsRequestData requestData, DataFetchingEnvironment env) { MyCustomContext dgsContext = DgsContext.getCustomContext(env); RequestContext requestContext = new RequestContext(); // 此处完成dgs上下文到你自定义RequestContext的属性复制 requestContext.setRoles(dgsContext.getRoles()); requestContextHolder.setRequestContext(requestContext); } @Override public void postProcess(DgsRequestData requestData, Object result) { // 必须清理,防止线程复用导致的上下文串用和内存泄漏 requestContextHolder.clear(); } }
完成配置后,你可以在任意业务类中直接注入RequestContextHolder,调用getRequestContext()即可获取当前请求的上下文,无需传递参数。
方案2:基于Spring请求作用域Bean实现(更简洁)
利用Spring原生的request作用域,直接将上下文定义为每个请求独立实例的Bean,Spring会自动通过代理保证单例Bean注入时拿到当前请求的上下文实例,无需手动处理ThreadLocal。
定义请求级上下文Bean
@Component @Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS) public class RequestContext { // 自定义上下文属性 private List<String> roles; // 其他用户/权限相关属性 // getter和setter }
新增DGS拦截器完成上下文赋值
@Component public class ContextInitInterceptor implements DgsRequestInterceptor { // 注入的是代理对象,实际指向当前请求对应的RequestContext实例 private final RequestContext requestContext; public ContextInitInterceptor(RequestContext requestContext) { this.requestContext = requestContext; } @Override public void preProcess(DgsRequestData requestData, DataFetchingEnvironment env) { MyCustomContext dgsContext = DgsContext.getCustomContext(env); // 直接给当前请求的上下文赋值 requestContext.setRoles(dgsContext.getRoles()); // 其他属性赋值 } }
该方案使用更简单,你可以在任意需要上下文的地方直接注入RequestContext即可使用,Spring会自动管理Bean的生命周期,不需要手动清理,天然线程安全。
注意事项
如果业务存在异步逻辑,需要额外配置线程池的TaskDecorator完成上下文在异步线程的传递,同步场景下上述两种方案可直接使用。
内容的提问来源于stack exchange,提问作者zeng
相关产品推荐
相关产品推荐

