我需要獲取代理會話作用域的012,317,517,476,676,838,上的bean。目標是當它被破壞時(通過invalidate()
或超時)進行會話清理。我添加了ContextLoaderListener
來公開上下文,並通過WebApplicationContextUtils.getWebApplicationContext()
獲得了bean。在會話超時時無法獲取會話作用域bean
一切工作正常,如果我自己在Servlet中使自己無效,但會話超時時,我得到一個Scope 'session' is not active for the current thread;
。我明白,問題發生在清理由Servlet引擎的內部線程完成,但我仍然需要能夠從HttpSessionListener
獲取此bean。
我似乎有很多相同的問題,沒有人得到解決方案,這就是爲什麼我再次問。
我的applicationContext.xml沒有bean聲明,因爲我使用了註釋。
這個豆我需要會話時候訪問了:
@Component
@Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
public class Access {
static private int SERIAL = 0;
private int serial;
public Access() {
serial = SERIAL++;
}
public int getSerial() {
return serial;
}
}
這是控制器,要麼create
或手動destroy
會話。
@Controller
public class Handler {
@Autowired
Access access;
@RequestMapping("/create")
public @ResponseBody String create() {
return "Created "+access.getSerial();
}
@RequestMapping("/destroy")
public @ResponseBody String destroy(HttpSession sess) {
int val = access.getSerial();
sess.invalidate();
return "Destroyed "+val;
}
}
而這正是我需要訪問Access
會議的內容的HttpSessionListener是聽會遭到破壞,範圍的bean。
public class SessionCleanup implements HttpSessionListener {
@Override
public void sessionDestroyed(HttpSessionEvent ev) {
// Get context exposed at ContextLoaderListener
WebApplicationContext ctx = WebApplicationContextUtils
.getWebApplicationContext(ev.getSession().getServletContext());
// Get the beans
Access v = (Access) ctx.getBean("access");
// prints a not-null object
System.out.println(v);
// this line raise the exception
System.out.println(v.getSerial());
}
@Override
public void sessionCreated(HttpSessionEvent ev) {/*Nothing to do*/}
}
以下例外情況發生在v.getSerial()
。
Ago 14, 2012 11:44:58 PM org.apache.catalina.session.StandardSession expire
SEVERE: Session event listener threw exception
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.access': Scope 'session' 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 model.Access$$EnhancerByCGLIB$$438f41a5.toString(<generated>)
at java.lang.String.valueOf(String.java:2902)
at java.io.PrintStream.println(PrintStream.java:821)
at org.apache.tomcat.util.log.SystemLogHandler.println(SystemLogHandler.java:242)
at service.SessionCleanup.sessionDestroyed(SessionCleanup.java:24)
at org.apache.catalina.session.StandardSession.expire(StandardSession.java:709)
at org.apache.catalina.session.StandardSession.isValid(StandardSession.java:576)
at org.apache.catalina.session.ManagerBase.processExpires(ManagerBase.java:712)
at org.apache.catalina.session.ManagerBase.backgroundProcess(ManagerBase.java:697)
at org.apache.catalina.core.ContainerBase.backgroundProcess(ContainerBase.java:1364)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1649)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1658)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1658)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run(ContainerBase.java:1638)
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.SessionScope.get(SessionScope.java:90)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:328)
... 19 more
最後,這裏是我的的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"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>session-listener-cleanup</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-config.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>service.SessionCleanup</listener-class>
</listener>
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>1</session-timeout>
</session-config>
</web-app>
正如我已經說過了,一切都很好,當我在失效控制器的方法destroy
會話。
更新1:可能的解決方案中
的問題發生,因爲需要以Spring能夠訪問會話bean的請求。事件雖然我們有一個關聯到線程的上下文,但沒有請求。
這裏有一些可能的選擇:
- 實現接口DisposableBean的,由alexwen建議。這意味着將業務邏輯移至模型對象 [here];
- 執行
DestructionAwareBeanPostProcessor
也建議alexwen。這意味着在做任何處理之前,你需要檢查被丟棄的豆是否是Access
; [here]; - 直接從會話中檢索bean。這種方式並不是很好,因爲您使用無證行爲來實現結果,但確實有效 [here];
- 模擬一個servlet請求並通過
RequestContextHolder
將其屬性綁定到Thread。這也導致無證行爲,在將來的版本中可以改變 [here];
我沒有選擇最後兩個,因爲他們沒有記錄。另外,我不喜歡在特定的bean之後清理每個bean的想法。因爲我也不想將業務邏輯混合到我的模型bean中,所以我最終創建了一個創建bean的@Service
,並且還有一個destroy
方法。
該方法由處理訪問bean負責。我在Access
上實現DisposableBean
接口,並將AccessManager
服務注入Access
bean並調用服務destroy
方法。該服務是這樣的:
@Service
public class AccessManager {
@Bean(name="access", destroyMethod="destroy")
@Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
@Autowired
public Access create(HttpServletRequest request) {
// creation logic
}
public void destroy(Access access) {
// disposal logic
}
}
正如例外情況所示,在web.xml中,應該使用DispatcherServlet或RequestContextListener/RequestContextFilter公開所有http請求並將請求綁定到線程。 – yorkw 2012-08-15 04:17:05
@yorkw監聽器已經註冊,如果我使會話無效,一切都會正常進行。問題是超時。我已更新該帖子以包含我的** web.xml **。 – 2012-08-15 12:24:47