javax.naming.NamingException: Cannot create resource instance
at org.apache.naming.factory.ResourceEnvFactory.getObjectInstance...
at org.apache.catalina.core.StandardContext.listenerStart...
HttpSessionListener 由 Servlet 容器(Tomcat)實例化,**早於 Spring 容器初始化完成**。監聽器構造時嘗試注入 Spring Bean(如 @Autowired UserService),但 Spring 尚未就緒,導致 JNDI 查找失敗。
public class SessionListener implements HttpSessionListener {
private UserService userService;
@Override
public void sessionDestroyed(HttpSessionEvent se) {
// 1. 獲取 ServletContext
ServletContext sc = se.getSession().getServletContext();
// 2. 獲取 Spring WebApplicationContext
WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(sc);
// 3. 手動獲取 Bean
if (userService == null) {
userService = wac.getBean(UserService.class);
}
// 4. 執行業務邏輯
userService.updateUserStatusOffline(se.getSession().getId());
}
@Override
public void sessionCreated(HttpSessionEvent se) { /* 同理 */ }
}
org.springframework.web.context.support.WebApplicationContextUtilsorg.springframework.web.context.WebApplicationContext<listener>
<listener-class>com.example.listener.SessionListener</listener-class>
</listener>
<!-- web.xml -->
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<!-- 監聽器作為 Spring Bean 定義,需實現 ApplicationListener 延遲注入 -->
但手動 WebApplicationContextUtils.getBean() 更簡單通用,推薦使用。
wac != null,防止上下文銷毀後調用。ServletContext 或使用 @Autowired 在 Spring 管理的組件中代理調用。理解容器啟動順序,在監聽器/Filter 中正確獲取 Spring Bean,是 Java Web 開發必備技能。