2012-10-26 29 views
0

我們正在努力使Servlet的3.0「異步支持」在我們的Spring應用程序:將Spring EntityManager放入異步Servlet 3.0的線程中?

  • 添加在web.xml (異步支持)真(/異步支持)所有servlet和過濾器。

  • 改寫了請求處理代碼

**

request.getAsyncContext().start(new Runnable() { 
    @Override 
    public void run() { 
    handleRequest(servlet, request, response); 
    } 
}); 

**

這會導致兩個問題:

  1. Spring Security認證爲l ost運行異步代碼時。

  2. 沒有EntityManager/session/...也沒有。

當然這兩個問題都是由於處理程序代碼是不是在同一個線程創建了Runnable線程執行的事實。理想情況下,工作線程應該從調用線程「繼承」Spring上下文。

我可以通過在構建時保存身份驗證並在執行時設置身份驗證來解決Spring Security問題。

private final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 

try { 
    SecurityContextHolder.clearContext(); 
    SecurityContextHolder.getContext().setAuthentication(authentication); 

    ..run code here.. 

} finally { 
    SecurityContextHolder.clearContext(); 
} 

但是,對於第二個問題,我不知道如何「轉移」會議的執行線程。對於servlet 2.x,我們使用OpenEntityManagerInViewFilter將會話粘貼到線程。

<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> 

但現在當然這沒有意義了,因爲它堅持它的線程是不一樣的,其執行請求的一個。

我試過各種版本的Spring 3.1和3.2。但沒有效果。

誰知道如何解決這個問題?最好用黑客。 :-)

的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

您需要打開該異步線程(會話不能在線程之間共享),一個新的會話/ EntityManager的。爲此,您可能需要使用OpenSessionInViewInterceptor(或者我猜JPA稱爲OpenEntityManagerInViewInterceptor),但您需要手動調用其方法或爲此編寫自己的方面並使用代理。

+0

我通過複製/粘貼過濾器的代碼獲得了某種成功。但是,我不知道下游的實體管理器會發生什麼 - 如果發生另一個異步處理。在下游出現故障時如何保證交易完整性? –

+0

事務完整性在這裏不是問題,因爲所有事務都將在Spring中由服務層或攔截器啓動的事務中完成,即使這發生在另一個線程中。當談到共享EntityManager時,除非你想遍歷所有線程並檢查它們的ThreadLocals,它是純Java,你只需要小心,否則你不可能強制在線程之間共享它。 –

+0

我不知道我是否真的想這樣做。 :-)我希望Spring 3.2爲此提供支持。 –