此文已由作者易國強授權網易雲社區發布。
歡迎訪問網易雲社區,了解更多網易技術產品運營經驗。
Bean 的順序加載
有些場景中,我們希望編寫的Bean能夠按照指定的順序進行加載。比如,有UserServiceBean和OrderServiceBean,我們需要在OrderServiceBean中調用UserServiceBean,獲取其提供的一些數據信息。針對這一場景,通常來說,有這么幾種方式:
1、將UserServiceBean封裝成一個服務類(如采用@Service注解),然后在OrderServiceBean中引入這個服務類,直接調用即可,簡單快捷。示例如下所示:
@Service("userServiceBean") public class UserServiceBean { public String print() { System.out.println("this is UserServiceBean print"); return "print ok"; } } @Service("orderServiceBean") public class OrderServiceBean { @Resource UserServiceBean userServiceBean; public void invoke(){ String ret = userServiceBean.print(); System.out.println("this is OrderServiceBean invoke " + ret ); } }
2、然而有些時候,我們的xxxServiceBean是沒有封裝成服務的,只是作為一個單純的Bean注入到Spring容器中。這個時候如果我們需要使用這個Bean實例,通常會考慮直接從ApplicationContext中以getBean("xxxServiceBean")的方式獲取。
在傳統的項目中,我們一般都會在xml配置文件中注入xxxServiceBean,這個時候Spring容器會依據xml中代碼編寫的順序依次加載各個Bean,示例如下所示:
<!-- 按代碼編寫順序依次加載 --><!-- 訂單服務Bean --><bean id="orderServiceBean" class="com.example.a.OrderServiceBean"></bean><!-- 演示服務--><bean id="depService" class="com.example.a.DepService"></bean><!-- 演示服務--><bean id="demoService" class="com.example.a.OtherDemoServiceImpl"></bean><!-- 用戶服務Bean--><bean id="userServiceBean" class="com.example.a.UserServiceBean"></bean>
在各構造函數中加入日志輸出可發現,會按照順序依次加載。如下圖所示:
- 如果我們在OrderServiceBean中有調用UserServiceBean,那么UserServiceBean則會優先於DepService和OtherDemoServiceImpl加載,調用代碼如下: ```public class OrderServiceBean {public OrderServiceBean() { System.out.println("OrderServiceBean constructor init."); UserServiceBean userServiceBean = SpringContextHolder.getBean("userServiceBean"); String ret = userServiceBean.print(); System.out.println("this is OrderServiceBean invoke " + ret ); } } ``` 這個時候觀察加載的順序如下圖所示: - 在Spring Boot項目中,我們一般用@Configuration + @Bean注解的方式來替代xml中Bean的注入,這個時候定義Bean的加載順序也很簡單,在同一個配置類中,也是按照代碼的編寫順序加載實例化的。示例如下所示: ```@Configurationpublic class MyConfigs {@Bean("userServiceBean")public UserServiceBean userServiceBean(){ return new UserServiceBean(); }@Bean("orderServiceBean")public OrderServiceBean orderServiceBean(){ return new OrderServiceBean(); } ```
有這么一個使用場景,如果UserServiceBean 采用@Bean + @Configuration的方式注入,而OrderServiceBean采用@Service注解的形式提供服務,同時在OrderServiceBean中仍然通過ApplicationContext的getBean()方式獲取UserServiceBean的示例,那么在編譯時候會報如下錯誤:
其中SpringContextHolder.java的代碼如下所示:
@Component("springContextHolder")public class SpringContextHolder implements ApplicationContextAware { private static ApplicationContext applicationContext; /** * 實現ApplicationContextAware接口的context注入函數, 將其存入靜態變量. */ @Override public void setApplicationContext(ApplicationContext applicationContext) { SpringContextHolder.applicationContext = applicationContext; } /** * 取得存儲在靜態變量中的ApplicationContext. */ public static ApplicationContext getApplicationContext() { checkApplicationContext(); return applicationContext; } /** * 從靜態變量ApplicationContext中取得Bean, 自動轉型為所賦值對象的類型. */ @SuppressWarnings("unchecked") public static <T> T getBean(String name) { checkApplicationContext(); return (T) applicationContext.getBean(name); } private static void checkApplicationContext() { if (applicationContext == null) { throw new IllegalStateException("applicaitonContext未注入,請在applicationContext.xml中定義SpringContextUtil"); } } }
這個時候,我們需要在OrderServiceBean類前加入如下注解,表示此Bean依賴於springContextHolder實例的加載,代碼示例如下所示,再次編譯通過。
@Service @DependsOn("springContextHolder")public class OrderServiceBean { public OrderServiceBean() { System.out.println("OrderServiceBean constructor init."); UserServiceBean userServiceBean = SpringContextHolder.getBean("userServiceBean"); String ret = userServiceBean.print(); System.out.println("this is OrderServiceBean invoke " + ret ); } }
此外,如果需要指定一個Bean A 先於 Bean B加載,那么可以在Bean B類前加入@DependsOn("beanA"),指定依賴加載順序。
不足之處,歡迎指正,謝謝~
更多網易技術、產品、運營經驗分享請點擊。
相關文章:
【推薦】 如何能低成本地快速獲取大量目標用戶,而不是與競爭對手持久戰?
【推薦】 小白用shiro(1)