我有一个Spring应用程序,它根据请求上下文注入某些bean。在这个例子中,它是Facebook bean。
@RestController
@RequestMapping("facebook")
public class FacebookInjectionController {
@Autowired
private Facebook facebook;
@Autowired
private UserRepository userRepository;
@RequestMapping(method = RequestMethod.GET)
public List<String> blah() {
String firstName = facebook.userOperations().getUserProfile().getFirstName();
return Arrays.asList(firstName);
}
@RequestMapping(method = RequestMethod.GET, value = "complex")
public List<String> blah2() {
UserJwt principal = (UserJwt) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Stream<User> stream = StreamSupport.stream(userRepository.findAll().spliterator(), true);
return stream.filter(u -> u.getUid().equals(principal.getUid()))
.map(u ->
facebook.userOperations().getUserProfile().getFirstName()
).collect(Collectors.toList());
}
}
此代码将正常运行,但偶尔会出现以下错误:
2017-02-09 01:39:59.133错误40802-[o-auto-1-exec-2]o.a.c.c.c.[/]。[dispatcherServlet]:Servlet。路径为[]的上下文中servlet[dispatcherServlet]的service()引发异常[请求处理失败;嵌套异常为org.springframework.beans.factory.BeanCreationException:创建名为“scopedTarget.facebook”的bean时出错:当前线程的作用域“Request”未处于活动状态;如果要从单例引用此bean,请考虑为其定义作用域代理;嵌套异常为java.lang.IllegalStateException:未找到线程绑定请求:是否在实际web请求之外引用请求属性,还是在最初接收线程之外处理请求?如果您实际上在web请求中操作,并且仍然收到此消息,那么您的代码可能在DispatcherServlet/DispatcherPortlet之外运行:在这种情况下,请使用RequestContextListener或RequestContextFilter公开当前请求。]有根本原因
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/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
at org.springframework.web.context.request.AbstractRequestAttributesScope.get(AbstractRequestAttributesScope.java:41)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:340)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:35)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:187)
at com.sun.proxy.$Proxy137.userOperations(Unknown Source)
at com.roomsync.FacebookInjectionController.lambda$blah2$5(FacebookInjectionController.java:43)
at com.roomsync.FacebookInjectionController$$Lambda$10/2024009478.apply(Unknown Source)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175)
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502)
at java.util.stream.ReduceOps$ReduceTask.doLeaf(ReduceOps.java:747)
at java.util.stream.ReduceOps$ReduceTask.doLeaf(ReduceOps.java:721)
at java.util.stream.AbstractTask.compute(AbstractTask.java:316)
at java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:731)
at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:902)
at java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1689)
at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1644)
at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
我尝试了多种解决方案(包括Spring MVC:如何在生成的线程中使用请求范围的bean?),但没有一个有效。
有没有办法将请求范围的bean传递给lambda或其他线程?
去什么地方https://stackoverflow.com/users/1262865/john16384说我已将配置更改为:
@Bean
@Scope(value = "inheritableThreadScope", proxyMode = ScopedProxyMode.INTERFACES)
public ConnectionRepository connectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
throw new IllegalStateException("Unable to get a ConnectionRepository: no user signed in");
}
return getUsersConnectionRepository(connectionFactoryLocator).createConnectionRepository(authentication.getName());
}
@Bean
@Scope(value="inheritableThreadScope", proxyMode=ScopedProxyMode.INTERFACES)
public Facebook facebook(ConnectionFactoryLocator connectionFactoryLocator) {
Connection<Facebook> connection = connectionRepository(connectionFactoryLocator).findPrimaryConnection(Facebook.class);
return connection != null ? connection.getApi() : null;
}
@Bean
@Scope(value = "inheritableThreadScope", proxyMode = ScopedProxyMode.INTERFACES)
public ExecutorService fbExecutor () {
return Executors.newSingleThreadExecutor();
}
控制器现在看起来像:
@RestController
@RequestMapping("facebook")
public class FacebookInjectionController {
@Autowired
private Facebook facebook;
@Autowired
private UserRepository userRepository;
@Autowired
private ExecutorService fbExecutor;
@RequestMapping(method = RequestMethod.GET)
public List<String> blah() {
String firstName = facebook.userOperations().getUserProfile().getFirstName();
return Arrays.asList(firstName);
}
@RequestMapping(method = RequestMethod.GET, value = "complex")
public List<String> blah2() throws ExecutionException, InterruptedException {
UserJwt principal = (UserJwt) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Stream<User> stream = StreamSupport.stream(userRepository.findAll().spliterator(), true);
Future<List<String>> submit = fbExecutor.submit(() -> stream.filter(u -> u.getUid().equals(principal.getUid()))
.map(u ->
facebook.userOperations().getUserProfile().getFirstName()
)
.collect(Collectors.toList()));
return submit.get();
}
}
我还有以下配置:
@Configuration
public class BeanFactoryConfig implements BeanFactoryAware {
private static final Logger LOGGER = Logger.getLogger(BeanFactoryConfig.class);
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof ConfigurableBeanFactory) {
// logger.info("MainConfig is backed by a ConfigurableBeanFactory");
ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;
/*Notice:
*org.springframework.beans.factory.config.Scope
* !=
*org.springframework.context.annotation.Scope
*/
org.springframework.beans.factory.config.Scope simpleThreadScope = new SimpleThreadScope() {
@Override
public void registerDestructionCallback(String name, Runnable callback) {
RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
attributes.registerDestructionCallback(name, callback, 3);
}
};
cbf.registerScope("inheritableThreadScope", simpleThreadScope);
/*why the following? Because "Spring Social" gets the HTTP request's username from
*SecurityContextHolder.getContext().getAuthentication() ... and this
*by default only has a ThreadLocal strategy...
*also see https://stackoverflow.com/a/3468965/923560
*/
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
}
else {
// logger.info("MainConfig is not backed by a ConfigurableBeanFactory");
}
}
}
即使这样,有时也会出现错误:
{
"timestamp": 1486686875535,
"status": 500,
"error": "Internal Server Error",
"exception": "java.util.concurrent.ExecutionException",
"message": "org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.facebook' defined in class path resource [com/roomsync/config/SocialConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.social.facebook.api.Facebook]: Factory method 'facebook' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.connectionRepository': Scope 'inheritableThreadScope' 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/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.",
"path": "/facebook/complex"
}
因此,我似乎仍然缺少激活作用域并将线程本地上下文复制到它的部分
这就是我在fork连接的线程中传输请求bean的方法。该示例仅用于说明。
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
// org.slf4j:slf4j-api:1.7.30
import org.slf4j.MDC;
// org.springframework:spring-web:5.2.12.RELEASE
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
class Scratch {
public static void main(String[] args) {
RequestAttributes context = RequestContextHolder.currentRequestAttributes();
Map<String, String> contextMap = MDC.getCopyOfContextMap();
List<String> list = new ArrayList<>();
list.parallelStream().map(id -> {
try {
// copy all required for spring beans
RequestContextHolder.setRequestAttributes(context);
MDC.setContextMap(contextMap);
// ************************************
// Spring request beans usage goes here
// ************************************
return 1;
} finally {
// clean all from thread local
MDC.clear();
RequestContextHolder.resetRequestAttributes();
}
})
.collect(Collectors.toList());
}
}
是否需要并行处理流?这会导致lambda可能在另一个线程中执行。
Stream stream=StreamSupport.stream(userRepository.findAll(). spliterator(), false);
发生了两件事:
1) Java流使用公共的Fork/Join池来并行执行事情。这些线程不是由Spring框架(或您)创建的。
2) 使用ThreadLocal支持请求范围的bean。
这意味着如果不是由Spring创建的线程尝试访问请求范围的bean,则不会找到它,因为线程不知道它(它不在ThreadLocal中)。
为了解决这个问题,您需要控制哪些线程用于流。一旦实现了这一点,就可以制作请求范围bean的副本以用于子线程。在线程完成其任务后,您还需要再次清理它们,否则您可能会留下bean,在该线程上执行的下一个任务可能会看到这些bean。
要更改并行流使用的线程,请参阅:Java 8并行流中的自定义线程池
我认为,如何正确配置Spring以将请求范围的bean传播到您已经找到的子线程。
目前正在用Spring Boot 2.0.0.M4、Spring 5.0.0.RC4和Reactor 3.1.0.RC1进行反应性编程。 如果没有,这是计划好的吗? 谢谢你抽出时间。
在我的Spring Boot应用程序中,我有一个调度器任务,每隔一小时执行一次。在调度器方法中尝试访问请求作用域bean。总是获取异常org.springframework.beans.factory.BeanCreationException。 下面是代码示例。 会有帮助的..谢了。
本文向大家介绍请问Spring中Bean的作用域有哪些?相关面试题,主要包含被问及请问Spring中Bean的作用域有哪些?时的应答技巧和注意事项,需要的朋友参考一下 考察点:框架 参考回答: 在Spring的早期版本中,仅有两个作用域:singleton和prototype,前者表示Bean以单例的方式存在;后者表示每次从容器中调用Bean时,都会返回一个新的实例,prototype通常翻译为原
我试图弄清楚spring是如何将线程安全的请求/会话范围的bean注入控制器组件(即通过方法访问这些bean的单线程和多线程)的 作为例子,考虑<代码> HttpServletRequest 字段,该控件标记为“代码> @ AutoWordEng/代码>注释(我知道将控制器与servlet API配对是不好的,但在学习目的上可以)。我了解到这样的bean是使用CGLib代理的,但仍然无法弄清楚代理
问题内容: 我正在通过jQuery的$ .ajax函数使用的第三方API上调用POST。但是,当我拨打电话时,出现以下错误: 我从这篇文章中看到这可能是Webkit的错误,所以我在Firefox中尝试了此操作(我正在使用Chrome开发),并且得到了相同的结果。 在这篇文章中,我还尝试通过将$.ajax函数的属性设置为并将设置为来使用jsonp。但是,这导致了500个内部服务器错误。 当我使用–d
我们有一个应用程序正在使用。我试图将其更改为较新的版本。如果我没有指定或者如果我指定了一个作用域,那么应用程序可以正常工作。我在请求多个作用域(如)时遇到一个问题,该作用域在以前的版本中适用。 我请求的客户端具有所有权限。 当我使用时,为了获得一个令牌,我经常执行get调用,如 如果我用或来尝试它,它工作得很好,因为或是客户端作用域的一部分。 如果我想在中请求多个作用域,我该怎么做?