CDI和Spring的主要差別

繼上一篇文章《Java元註解meta-annotation與依賴注入》,我又探索了兩大依賴注入技術體系CDI和Spring的關係。Spring實現了CDI規範嗎?相信你們也會有這種問題。html

事實上有兩個規範,一個JSR-299 CDI規範,一個JSR-330 DI規範,繞吧,是挺繞。Spring好像只實現了DI規範,而沒有實現CDI規範。那麼CDI和DI、CDI和Spring都有什麼差別呢?java

CDI和DI的差別,主要表如今DI主要定義了5個註解(@Inject, @Named, @Qualifier, @Scope, @Singleton)及語義,而CDI則主要定義了scope, interceptor, bean lifecycle callback, EL integration, Java EE integration等API、SPI和語義,DI不特定於Java EE,而CDI特定於Java EE。編程

CDI和Spring的差別,例如,CDI定義了幾個標準scope(request, session, application, conversation),容許自定義其餘scope但只能看成「pseudo-scope」,而沒有Spring的prototype和singleton。而Spring不僅有prototype和singleton,又在Web MVC模塊裏定義了幾個scope(request, session, globalSession, application),所以雙方在scope方面是不一樣的。session

此次主要講一個我認爲很大的差別,即CDI的Client Proxy機制,見文檔https://docs.jboss.org/cdi/sp...app

Client Proxy機制的特色是,若是爲一個UserController注入了一個UserService bean,這個UserService實際上不是真正的UserService對象,而是一個代理對象,示例代碼以下:框架

@Named
public class UserController {
    // 這個不是真正的UserService對象,而是一個代理
    @Inject
    private UserService userService;
}

@Named
public class UserService {
}

爲何要這麼代理一下呢?代理的做用是動態化,當UserController被實例化時,它只持有一個UserService代理的引用,此時UserService無需從上下文查找,甚至無需實例化(CDI的bean都是lazy creation的),只有當UserController在其方法中真的要使用UserService時,纔到上下文中查找UserService,若沒有就實例化一個。這一層抽象容許scope較大的bean引用scope較小的bean,例如UserController能夠是application scope,而UserService能夠是request scope,只要UserController在真的使用UserService時剛好進入了request scope就能夠。函數式編程

Seam框架(CDI規範的起源之一)甚至還支持雙向注入注出,即若是UserController的userService field被修改爲另外一個值(指向一個新的UserService實例),那麼當UserController的方法執行結束後,這個新值會被寫回上下文,使得上下文中的UserService實例被替換。這一機制也是利用了代理,Seam給全部的client proxy都加了AOP interceptor來實現以上機制。函數

作個有條理的對比,CDI是如此:單元測試

  1. 對於幾種標準scope,注入的是client proxy而非真實的bean,注入後仍會動態跟蹤上下文的變更
  2. lazy creation
  3. 不管UserController是哪種標準scope,都只有在真的用到某個依賴項時才知道其是否有效
  4. scope較大的bean容許依賴scope較小的bean
  5. 標準scope的bean都容許循環依賴(利用代理來作實時解環處理)

Spring顯然並不是如此:測試

  1. 默認注入的是真實的bean而非client proxy,注入後就再也不跟蹤上下文的變更
  2. 默認eager creation,可用@Lazy註解配置爲lazy creation
  3. 若是UserController是singleton scope(默認值),那麼在應用啓動時就會校驗它所要注入的依賴是否有效
  4. scope較大的bean不容許依賴scope較小的bean
  5. 只有singleton scope的bean容許循環依賴(應用啓動時用兩階段初始化來作解環處理)

Spring的風格很有函數式編程的不可變性,而CDI自動啓用的可變性代理則顯得有些多餘。事實上市場也選擇了Spring。(其實如今的CDI比Spring還要輕量級,new一個就能用,包括單元測試環境,而Spring卻須要@SpringBootTest註解,那麼究竟差距在哪裏呢?)

相關文章
相關標籤/搜索