我们正在使用带有 flex 作为客户端的 glassfish/websphere 的 spring mvc。其中一个 Web 服务需要向客户端返回指示,然后继续异步执行而不需要返回任何内容。
我们尝试使用纯 Java 线程,但它不起作用,因为 Spring 无法访问 Web 应用程序上下文。
然后我们尝试以“弹簧方式”(使用 @async + )使用线程,但弹簧抛出异常:
2013 年 12 月 11 日 16:11:44,769 [SimpleAsyncTaskExecutor-1] 错误 com.app.closeQuestionnaire.logic.CloseQuestionnaireBusinessActionsService.handleCRMEventsSending(行:254)[userrrr | 30605808 | 2c90908b424ca07a01424ca374c00000 ] - [问卷 id: 2c90908b424ca07a01424ca374c00000] 的 sendCrmEvent 请求失败,异常:创建名称为“scopedTarget.sessionCachedUserMetadata”的 bean 时出错:范围“会话”对于当前线程无效;如果您打算从单例中引用它,请考虑为该 bean 定义一个作用域代理;嵌套异常是 java.lang.IllegalStateException: No thread-bound request found: 你指的是实际 Web 请求之外的请求属性,或在原始接收线程之外处理请求?如果您实际上是在 Web 请求中操作并且仍然收到此消息,则您的代码可能在 DispatcherServlet/DispatcherPortlet 之外运行:在这种情况下,请使用 RequestContextListener 或 RequestContextFilter 来公开当前请求。(10065) com.app.questionnaire.exceptions.CRMEventException: sendCrmEvent 请求 [questionnaire id: 2c90908b424ca07a01424ca374c00000] 失败并出现异常:创建名称为“scopedTarget.sessionCachedUserMetadata”的 bean 时出错:范围“会话”对于当前线程无效;如果您打算从单例中引用它,请考虑为该 bean 定义一个作用域代理;嵌套异常是 java.lang.IllegalStateException: No thread-bound request found: 您是指实际 Web 请求之外的请求属性,还是在原始接收线程之外处理请求?如果您实际上是在 Web 请求中操作并且仍然收到此消息,则您的代码可能在 DispatcherServlet/DispatcherPortlet 之外运行:在这种情况下,请使用 RequestContextListener 或 RequestContextFilter 来公开当前请求。在 com.app.closeQuestionnaire.logic.CloseQuestionnaireBusinessActionsService.handleCRMEventsLogics(CloseQuestionnaireBusinessActionsService.java:780) 在 com.app.closeQuestionnaire.logic.CloseQuestionnaireBusinessActionsService.handleCRMEventsSending(CloseQuestionnaireBusinessActionsService.java:251) 在 com.app.closeQuestionnaire.logic.CloseQuestionnaireBusinessActions FastClassByCGLIB $729836cc.invoke() 在 net.sf。
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_5.xsd"
version="2.5">
<display-name>advisor-server</display-name>
<!-- ========= Context parameters ========= -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:ApplicationContext.xml</param-value>
</context-param>
<context-param>
<description>Parameter specifying the location of the log4j
configuration file</description>
<param-name>log4jConfigLocation</param-name>
<param-value>file:${advisorLog4jConfigLocation}</param-value>
</context-param>
<!-- ========= Listeners ========= -->
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener
</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>
<listener>
<display-name>serviceRefRegistrar</display-name>
<listener-class>com.app.application.ServiceRefRegistrar
</listener-class>
</listener>
<!-- Loads configuration to make sure we don't crash during runtime in case
of parsing errors -->
<listener>
<listener-class>com.app.application.ApplicationConfigListener
</listener-class>
</listener>
<!-- ========= filters ========= -->
<filter>
<filter-name>requestContextFilter</filter-name>
<filter-class>org.springframework.web.filter.RequestContextFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>requestContextFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>LoggingFilter</filter-name>
<filter-class>com.app.general.logging.LoggingFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>LoggingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>AdviserRequestFilter</filter-name>
<filter-class>com.app.commons.application.AdviserRequestFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>AdviserRequestFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
春天.xml
<beans >
<!-- Context -->
<context:component-scan base-package="com.modelity.advisor" />
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- Activates @Scheduled and @Async annotations for scheduling -->
<task:annotation-driven />
</beans>
@Component
public class PostClosePreformerProcess{
@Autowired
private QuestionnaireBusinessService questionnaireService;
public PostClosePreformerProcess() {
}
@Async
public void doClose(String questionnaireId) {
try {
questionnaireService.postCloseQuestionnaire(questionnaireId);
} catch (Exception e) {
AdvisorLogger.error(this, "Post Closed Failed", e.getMessage(), e);
}
}
public QuestionnaireBusinessService getQuestionnaireService() {
return questionnaireService;
}
public void setQuestionnaireService(
QuestionnaireBusinessService questionnaireService) {
this.questionnaireService = questionnaireService;
}
}
@Service
public class CloseQuestionnaireBusinessService {
@Autowired
private PostClosePreformerProcess postClosePreformerProcess;
public PreCloseQuestionnaireDto preCloseQuestionnaire(String questionnaireId) {
postClosePreformerProcess.doClose(questionnaireId);
}
}
编辑:确实我们需要留在会话/请求范围内,因为异步(线程)部分需要使用在会话范围内定义的应用程序内的bean,例如bean之一(这是异常抱怨的):
@Component("sessionCachedUserMetadata")
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Lazy(true)
public class UserMetadataSessionCachedService implements UserMetadataBusinessService {