1. 程式人生 > >Spring Boot 學習系列(09)—自定義Bean的順序加載

Spring Boot 學習系列(09)—自定義Bean的順序加載

rri 學習 內容安全 master sys const nco 單純 分享圖片

此文已由作者易國強授權網易雲社區發布。

歡迎訪問網易雲社區,了解更多網易技術產品運營經驗。

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>

      在各構造函數中加入日誌輸出可發現,會按照順序依次加載。如下圖所示:

![image](https://github.com/siyuyifang/image/blob/master/spring-boot/9/9-1.png?raw=true)- 如果我們在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 );
}
}
```
這個時候觀察加載的順序如下圖所示:




![image](https://github.com/siyuyifang/image/blob/master/spring-boot/9/9-2.png?raw=true)- 在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"),指定依賴加載順序。

  • 不足之處,歡迎指正,謝謝~


免費體驗雲安全(易盾)內容安全、驗證碼等服務

更多網易技術、產品、運營經驗分享請點擊。



相關文章:
【推薦】 基於開源,強於開源,輕舟微服務解決方案深度解讀
【推薦】 當Shell遇上了NodeJS

Spring Boot 學習系列(09)—自定義Bean的順序加載