首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Spring Service 之間相互調用的最佳實踐

     編輯:Java 相關知識

問題背景

在傳統 Struts+Spring+Hibernate 架構中,Action 透過 getBean() 取得 Service。但若 Service A 需呼叫 Service B,直接 new ServiceB() 會導致 B 內部的 DAO 等依賴無法由容器注入。本文提供一種優雅的解決方案:靜態持有 ApplicationContext。

核心類別設計

1. AppContext - 全域靜態持有器

public class AppContext {
    private static ApplicationContext applicationContext;

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    public static void setApplicationContext(ApplicationContext ctx) {
        applicationContext = ctx;
    }
}

2. SpringBeanService - 服務定位器(單例)

public class SpringBeanService {
    private static SpringBeanService instance;
    private ApplicationContext applicationContext;

    private SpringBeanService() {}

    public static synchronized SpringBeanService getInstance() {
        if (instance == null) instance = new SpringBeanService();
        return instance;
    }

    public ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    public void setApplicationContext(ApplicationContext ctx) {
        this.applicationContext = ctx;
    }

    // 具體取得 Service 的便利方法
    public UserService getUserService() {
        return (UserService) AppContext.getApplicationContext().getBean("userService");
    }
}

3. ConfigLoadListener - Web 啟動時初始化

public class ConfigLoadListener implements ServletContextListener {
    public void contextInitialized(ServletContextEvent event) {
        try {
            WebApplicationContext ctx = WebApplicationContextUtils
                .getRequiredWebApplicationContext(event.getServletContext());
            AppContext.setApplicationContext(ctx);  // 關鍵:注入全域持有器

            // 可順便載入其他設定檔
            ServletContext sc = event.getServletContext();
            String path = sc.getInitParameter("setting.properties");
            Properties props = new Properties();
            props.load(sc.getResourceAsStream(path));
            GlobalConstant.setCmdbProperties(props);
        } catch (Exception e) {
            System.err.println("系統初始化失敗: " + e);
            System.exit(1);
        }
    }
    public void contextDestroyed(ServletContextEvent event) {}
}

在 web.xml 註冊該 Listener:

<listener>
  <listener-class>com.yourpkg.ConfigLoadListener</listener-class>
</listener>

Service 中調用範例

public class ServiceA {
    public void doSomething() {
        UserService userService = SpringBeanService.getInstance().getUserService();
        userService.someMethod();
    }
}

優缺點分析

優點缺點
零侵入:既有 Service 程式碼無需修改建構子或 Setter引入全域狀態,單元測試需額外 Mock ApplicationContext
適用於舊專案快速遷移違反純粹依賴注入原則,新專案建議改用 @Autowired 或建構子注入

現代替代方案(Spring 3+)

  • @Autowired 直接注入其他 Service(需確保兩者皆為 Spring Bean)
  • 建構子注入:public ServiceA(ServiceB serviceB) { this.serviceB = serviceB; }
  • 若確有跨容器調用需求,可考慮 ApplicationContextAware 介面搭配非靜態持有器

結語

靜態持有 ApplicationContext 是解決舊架構 Service 互調的實用手段,但在新專案中應優先採用標準依賴注入機制,保持程式碼純淨與可測試性。

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