问题
We're trying to enable Servlet's 3.0 "async-supported" in our Spring application:
Added in web.xml (async-supported)true(/async-supported) for all servlets and filters.
Rewrote the request handling code as
**
request.getAsyncContext().start(new Runnable() {
@Override
public void run() {
handleRequest(servlet, request, response);
}
});
**
This causes two problems:
Spring Security authentication is lost when running the asynchronous code.
There is no EntityManager/session/... any more either.
Of course both problems are due to the fact the handler code is not executed in the same thread as the thread that created the Runnable. Ideally the worker thread should "inherit" the Spring contexts from the invoking thread.
I could get around the Spring Security problem by saving the authentication at construction time and setting it at execution time.
private final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
try {
SecurityContextHolder.clearContext();
SecurityContextHolder.getContext().setAuthentication(authentication);
..run code here..
} finally {
SecurityContextHolder.clearContext();
}
However, for the second problem I have no clue how to "transfer" the session to the executing thread. For servlet 2.x we were using OpenEntityManagerInViewFilter to stick a session to the thread.
<filter>
<filter-name>openEntityManagerInViewFilter</filter-name>
<filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
<async-supported>true</async-supported>
<init-param>
<param-name>entityManagerFactoryBeanName</param-name>
<param-value>entityManagerFactory</param-value>
</init-param>
</filter>
But now of course this does not make sense any more because the thread it sticks it to is not the same as the one which executed the request.
I've tried various versions of Spring 3.1 and 3.2. But to no effect.
Anybody who knows how to get around this ? Preferably with hacking. :-)
My web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<servlet>
<servlet-name>Foo</servlet-name>
<servlet-class>com.foo.Main</servlet-class>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>Foo</servlet-name>
<url-pattern>/restricted/*</url-pattern>
</servlet-mapping>
<filter>
<filter-name>openEntityManagerInViewFilter</filter-name>
<filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
<async-supported>true</async-supported>
<init-param>
<param-name>entityManagerFactoryBeanName</param-name>
<param-value>entityManagerFactory</param-value>
</init-param>
</filter>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.bar.ServletInit</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<context-param>
<param-name>contextInitializerClasses</param-name>
<param-value>com.foo.SpringCtxInitializer</param-value>
</context-param>
<filter-mapping>
<filter-name>openEntityManagerInViewFilter</filter-name>
<url-pattern>/restricted/*</url-pattern>
</filter-mapping>
</web-app>
回答1:
You need to open a new session/entitymanager for that async thread (sessions can't be shared between threads). For this purpose you might want to use OpenSessionInViewInterceptor
(or I guess for JPA it's called OpenEntityManagerInViewInterceptor
), but you'll need to manually invoke its methods or write your own aspect for this purpose and use proxies.
来源:https://stackoverflow.com/questions/13086821/put-spring-entitymanager-in-thread-for-async-servlet-3-0