在进行中的项目中遇到了在kafka消费者中通过fegin调用外部服务的时候抛出以下形式的错误
org.springframework.beans.factory.support.ScopeNotActiveException:
Error creating bean with name 'scopedTarget.oauth2ClientContext':
Scope 'request' is not active for the current thread;
consider defining a scoped proxy for this bean if you intend to refer to it
from a singleton;
nested exception is java.lang.IllegalStateException:
No thread-bound request found:
Are you referring to request attributes outside of an actual web request,
or processing a request outside of the originally receiving thread?
If you are actually operating within a web request and still receive this message,
your code is probably running outside of DispatcherServlet: In this case,
use RequestContextListener or RequestContextFilter to expose the current request.
通过查找资料,如果在某个地方访问请求范围的 bean 并希望在 Web 请求范围之外重用那段代码,就会报上面的异常。
大概记录一下我解决这个问题而采取的方法:
发生上述错误是因为 spring 请求范围仅针对 Web 应用程序请求注册。通过 RequestAttribute 接口的实现 spring 从 httpRequest 中放置/检索请求范围信息。如果线程是在 Web 请求之外实例化的,则它在线程局部变量中没有所需的属性,因此会引发异常。
spring没有将Request Scope暴露在web请求之外的主要原因是没有明确的方法来确定请求什么时候开始,什么时候完成。例如,同一线程可以在多个请求处理中重用。并且在另一个请求开始之前,需要清除所有线程本地请求范围的变量。在这种情况下,应用程序开发人员最了解请求何时开始以及何时完成它的工作。但在 Web 请求过滤器的情况下,请求侦听器可用于确定请求的开始和结束。
所以可以通过一下步骤进行:
实现 RequestAttributes 接口:
它在内部维护一个 bean 的映射。如果它包含 bean,则返回该 bean,否则返回 null。由于我们希望本博客的上下文仅支持 Request-Scope,因此未实现与会话范围相关的方法。
public class CustomRequestScopeAttr implements RequestAttributes {
private Map<String, Object> requestAttributeMap = new HashMap<>();
@Override
public Object getAttribute(String name, int scope) {
if(scope== RequestAttributes.SCOPE_REQUEST) {
return this.requestAttributeMap.get(name);
}
return null;
}
@Override
public void setAttribute(String name, Object value, int scope) {
if(scope== RequestAttributes.SCOPE_REQUEST){
this.requestAttributeMap.put(name, value);
}
}
@Override
public void removeAttribute(String name, int scope) {
if(scope== RequestAttributes.SCOPE_REQUEST) {
this.requestAttributeMap.remove(name);
}
}
@Override
public String[] getAttributeNames(int scope) {
if(scope== RequestAttributes.SCOPE_REQUEST) {
return this.requestAttributeMap.keySet().toArray(new String[0]);
}
return new String[0];
}
@Override
public void registerDestructionCallback(String name, Runnable callback, int scope) {
// Not Supported
}
@Override
public Object resolveReference(String key) {
// Not supported
return null;
}
@Override
public String getSessionId() {
return null;
}
@Override
public Object getSessionMutex() {
return null;
}
}
在请求开始时,通过执行以下语句指示 RequestContextHolder 使用此 CustomRequestAttr。
RequestContextHolder.setRequestAttributes(new CustomRequestScopeAttr());
并在 finally 块中通过以下方式清除 requestAttributes。
RequestContextHolder.resetRequestAttributes();