В приложении Spring MVC у меня есть bean с запросом. Я вставляю этот bean где-нибудь. Там поток обслуживания HTTP-запросов может порождать новый поток.
Но всякий раз, когда я пытаюсь получить доступ к областью bean с учетом запроса из недавно созданного потока, я получаю org.springframework.beans.factory.BeanCreationException
(см. ниже трассировку стека).
Доступ к области запросов bean, зависящей от запроса, из потока запросов HTTP отлично работает.
Как сделать доступным для запросов bean для потоков, созданных потоком запросов HTTP?
Простая настройка
Получите следующие фрагменты кода. Затем запустите сервер, например, http://example.com:8080.
При обращении к http://example.com:8080/scopetestnormal каждый раз, когда запрос отправляется по этому адресу, counter
увеличивается на 1 (заметный через выход журнала).:) Супер!
При обращении к http://example.com:8080/scopetestthread каждый раз, когда запрос отправляется по этому адресу, вызывается указанные исключения.: (Независимо от того, что выбрано ScopedProxyMode
, это происходит как для CGLIB-based, так и для
JDK-динамический-прокси-интерфейс на основе запросов beans
Файл конфигурации
package com.example.config
@Configuration
@ComponentScan(basePackages = { "com.example.scopetest" })
public class ScopeConfig {
private Integer counter = new Integer(0);
@Bean
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public Number counter() {
counter = new Integer(counter.intValue() + 1);
return counter;
}
/* Adding a org.springframework.social.facebook.api.Facebook request-scoped bean as a real-world example why all this matters
@Bean
@Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
public Facebook facebook() {
Connection<Facebook> facebook = connectionRepository()
.findPrimaryConnection(Facebook.class);
return facebook != null ? facebook.getApi() : new FacebookTemplate();
}
*/
...................
}
Файл контроллера
package com.example.scopetest;
import javax.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.social.facebook.api.Facebook;
import org.springframework.social.facebook.api.FacebookProfile;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ScopeTestController {
//@Inject
//private Facebook facebook;
@Inject
private Number counter;
private static final Logger logger = LoggerFactory
.getLogger(ScopeTestController.class);
@RequestMapping(value = "/scopetestnormal")
public void scopetestnormal() {
logger.debug("About to interact with a request-scoped bean from HTTP request thread");
logger.debug("counter is: {}", counter);
/*
* The following also works
* FacebookProfile profile = facebook.userOperations().getUserProfile();
* logger.debug("Facebook user ID is: {}", profile.getId());
*/
}
@RequestMapping(value = "/scopetestthread")
public void scopetestthread() {
logger.debug("About to spawn a new thread");
new Thread(new RequestScopedBeanAccessingThread()).start();
logger.debug("Spawned a new thread");
}
private class RequestScopedBeanAccessingThread implements Runnable {
@Override
public void run() {
logger.debug("About to interact with a request-scoped bean from another thread. Doomed to fail.");
logger.debug("counter is: {}", counter);
/*
* The following is also doomed to fail
* FacebookProfile profile = facebook.userOperations().getUserProfile();
* logger.debug("Facebook user ID is: {}", profile.getId());
*/
}
}
}
Трассировка стека для CGLIB-ориентированного запроса bean (proxyMode = ScopedProxyMode.TARGET_CLASS
)
SLF4J: Failed toString() invocation on an object of type [$java.lang.Number$$EnhancerByCGLIB$$45ffcde7]
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.counter': 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/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:342)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:33)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.getTarget(Cglib2AopProxy.java:654)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:605)
at $java.lang.Number$$EnhancerByCGLIB$$45ffcde7.toString(<generated>)
at org.slf4j.helpers.MessageFormatter.safeObjectAppend(MessageFormatter.java:304)
at org.slf4j.helpers.MessageFormatter.deeplyAppendParameter(MessageFormatter.java:276)
at org.slf4j.helpers.MessageFormatter.arrayFormat(MessageFormatter.java:230)
at ch.qos.logback.classic.spi.LoggingEvent.<init>(LoggingEvent.java:114)
at ch.qos.logback.classic.Logger.buildLoggingEventAndAppend(Logger.java:447)18:09:48.276 container [Thread-16] DEBUG c.g.s.c.c.god.ScopeTestController - counter is: [FAILED toString()]
at ch.qos.logback.classic.Logger.filterAndLog_1(Logger.java:421)
at ch.qos.logback.classic.Logger.debug(Logger.java:514)
at com.example.scopetest.ScopeTestController$RequestScopedBeanAccessingThread.run(ScopeTestController.java:58)
at java.lang.Thread.run(Thread.java:722)
Caused by: 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:40)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:328)
... 14 more
Трассировка стека для JDK-динамического-прокси-интерфейса на основе запроса bean (proxyMode = ScopedProxyMode.INTERFACES
)
Exception in thread "Thread-16" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.facebook': 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/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:342)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:33)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:182)
at $Proxy28.userOperations(Unknown Source)
at com.example.scopetest.ScopeTestController$PrintingThread.run(ScopeTestController.java:61)
at java.lang.Thread.run(Thread.java:722)
Caused by: 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:40)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:328)
... 6 more