首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Spring 管理 Filter/Listener/Servlet 失效:@Resource 注入失敗的根因與解決方案

     編輯:程式設計綜合問答

問題現象

在 ServletContextListener 實現類中使用 @Resource 注入 Spring Bean(NoteTableDAO, NoteOperDAO),Tomcat 啟動拋出 NamingException: Cannot create resource instance,堆疊指向 DefaultInstanceManager.processAnnotations。改用 web.xml 配置 Listener 則正常。

根本原因

Servlet 容器管理的組件不在 Spring IOC 容器生命周期內:

  • Filter、Listener、Servlet 由 Servlet 容器(Tomcat)實例化、初始化、銷毀;
  • Spring 的 ContextLoaderListener 在 contextInitialized 中啟動 IOC 容器,**晚於** 容器創建 Listener 實例;
  • 容器實例化 Listener 時嘗試處理註解(@Resource、@Inject、@EJB 等),委託 JNDI 查找,**而 Spring Bean 未綁定到 JNDI**,導致 NamingException。

解決方案:手動從 Spring 容器獲取 Bean

public class ApplicationListener implements ServletContextListener {
    private NoteTableDAO noteTableDAO;
    private NoteOperDAO noteOperDAO;

    @Override
    public void contextInitialized(ServletContextEvent event) {
        ServletContext servletContext = event.getServletContext();
        // 關鍵:從 ServletContext 獲取 Spring WebApplicationContext
        WebApplicationContext springContext = WebApplicationContextUtils
            .getWebApplicationContext(servletContext);
        
        if (springContext != null) {
            // 手動獲取 Bean(按名稱或類型)
            noteTableDAO = springContext.getBean(NoteTableDAO.class);
            noteOperDAO = springContext.getBean(NoteOperDAO.class);
            
            // 業務邏輯...
            servletContext.setAttribute("noteTableMap", noteTableDAO.queryAllForMap());
            servletContext.setAttribute("noteOperMap", noteOperDAO.queryAllForMap());
        }
    }

    @Override
    public void contextDestroyed(ServletContextEvent event) {
        // 清理引用
        noteTableDAO = null;
        noteOperDAO = null;
    }
}

關鍵點解析

  1. WebApplicationContextUtils.getWebApplicationContext(servletContext):Spring 在啟動時將 IOC 容器綁定到 ServletContext 屬性 org.springframework.web.context.WebApplicationContext.ROOT 下,此工具類用於取出;
  2. 獲取時機:必須在 contextInitialized 中獲取,此時 Spring 容器已就緒;
  3. 獲取方式:推薦按類型 getBean(Class)(Spring 3.0+),避免硬編碼 Bean 名稱;
  4. 非 Spring 管理優勢:Listener 仍由容器管理生命周期,Spring 僅作為 Bean 供應者,職責清晰分離。

替代方案對比

方案優點缺點
手動 getBean(本文方案) 無侵入、標準、兼容所有容器 需手寫樣板代碼
@Autowired + SpringBeanAutowiringSupport 註解風格,自動注入 需繼承 SpringBeanAutowiringSupport,仍依賴 JNDI 查找
DelegatingFilterProxy / DelegatingListener 完全委託 Spring 管理 配置繁瑣,Listener 不直接支持
Spring Boot @ServletComponentScan 原生支持註解注入 需遷移至 Spring Boot

總結

Filter/Listener/Servlet 不要指望 Spring 自動注入。它們屬於容器管轄,Spring 啟動更晚。標準做法是在初始化階段通過 WebApplicationContextUtils 手動從 Spring 容器拉取所需 Bean。這也是 Spring 官方文檔推薦的模式。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved