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