《Spring實戰(第4版)》——2.3 通過Java代碼裝配bean
本節書摘來自異步社區《Spring實戰(第4版)》一書中的第2章,第2.3節,作者: 【美】Craig Walls(沃爾斯)著,更多章節內容可以訪問雲棲社區“異步社區”公眾號查看
2.3 通過Java代碼裝配bean
盡管在很多場景下通過組件掃描和自動裝配實現Spring的自動化配置是更為推薦的方式,但有時候自動化配置的方案行不通,因此需要明確配置Spring。比如說,你想要將第三方庫中的組件裝配到你的應用中,在這種情況下,是沒有辦法在它的類上添加@Component和@Autowired注解的,因此就不能使用自動化裝配的方案了。
在這種情況下,你必須要采用顯式裝配的方式。在進行顯式配置的時候,有兩種可選方案:Java和XML。在這節中,我們將會學習如何使用Java配置,接下來的一節中將會繼續學習Spring的XML配置。
就像我之前所說的,在進行顯式配置時,JavaConfig是更好的方案,因為它更為強大、類型安全並且對重構友好。因為它就是Java代碼,就像應用程序中的其他Java代碼一樣。
同時,JavaConfig與其他的Java代碼又有所區別,在概念上,它與應用程序中的業務邏輯和領域代碼是不同的。盡管它與其他的組件一樣都使用相同的語言進行表述,但JavaConfig是配置代碼。這意味著它不應該包含任何業務邏輯,JavaConfig也不應該侵入到業務邏輯代碼之中。盡管不是必須的,但通常會將JavaConfig放到單獨的包中,使它與其他的應用程序邏輯分離開來,這樣對於它的意圖就不會產生困惑了。
接下來,讓我們看一下如何通過JavaConfig顯式配置Spring。
2.3.1 創建配置類
在本章前麵的程序清單2.3中,我們第一次見識到JavaConfig。讓我們重溫一下那個樣例中的CDPlayerConfig:
創建JavaConfig類的關鍵在於為其添加@Configuration注解,@Configuration注解表明這個類是一個配置類,該類應該包含在Spring應用上下文中如何創建bean的細節。
到此為止,我們都是依賴組件掃描來發現Spring應該創建的bean。盡管我們可以同時使用組件掃描和顯式配置,但是在本節中,我們更加關注於顯式配置,因此我將CDPlayerConfig的@ComponentScan注解移除掉了。
移除了@ComponentScan注解,此時的CDPlayerConfig類就沒有任何作用了。如果你現在運行CDPlayerTest的話,測試會失敗,並且會出現BeanCreation- Exception異常。測試期望被注入CDPlayer和CompactDisc,但是這些bean根本就沒有創建,因為組件掃描不會發現它們。
為了再次讓測試通過,你可以將@ComponentScan注解添加回去,但是我們這一節關注顯式配置,因此讓我們看一下如何使用JavaConfig裝配CDPlayer和CompactDisc。
2.3.2 聲明簡單的bean
要在JavaConfig中聲明bean,我們需要編寫一個方法,這個方法會創建所需類型的實例,然後給這個方法添加@Bean注解。比方說,下麵的代碼聲明了CompactDisc bean:
@Bean注解會告訴Spring這個方法將會返回一個對象,該對象要注冊為Spring應用上下文中的bean。方法體中包含了最終產生bean實例的邏輯。
默認情況下,bean的ID與帶有@Bean注解的方法名是一樣的。在本例中,bean的名字將會是sgtPeppers。如果你想為其設置成一個不同的名字的話,那麼可以重命名該方法,也可以通過name屬性指定一個不同的名字:
不管你采用什麼方法來為bean命名,bean聲明都是非常簡單的。方法體返回了一個新的SgtPeppers實例。這裏是使用Java來進行描述的,因此我們可以發揮Java提供的所有功能,隻要最終生成一個CompactDisc實例即可。
請稍微發揮一下你的想象力,我們可能希望做一點稍微瘋狂的事情,比如說,在一組CD中隨機選擇一個CompactDisc來播放:
現在,你可以自己想象一下,借助@Bean注解方法的形式,我們該如何發揮出Java的全部威力來產生bean。當你想完之後,我們要回過頭來看一下在JavaConfig中,如何將CompactDisc注入到CDPlayer之中。
2.3.3 借助JavaConfig實現注入
我們前麵所聲明的CompactDisc bean是非常簡單的,它自身沒有其他的依賴。但現在,我們需要聲明CDPlayerbean,它依賴於CompactDisc。在JavaConfig中,要如何將它們裝配在一起呢?
在JavaConfig中裝配bean的最簡單方式就是引用創建bean的方法。例如,下麵就是一種聲明CDPlayer的可行方案:
cdPlayer()方法像sgtPeppers()方法一樣,同樣使用了@Bean注解,這表明這個方法會創建一個bean實例並將其注冊到Spring應用上下文中。所創建的bean ID為cdPlayer,與方法的名字相同。
cdPlayer()的方法體與sgtPeppers()稍微有些區別。在這裏並沒有使用默認的構造器構建實例,而是調用了需要傳入CompactDisc對象的構造器來創建CDPlayer實例。
看起來,CompactDisc是通過調用sgtPeppers()得到的,但情況並非完全如此。因為sgtPeppers()方法上添加了@Bean注解,Spring將會攔截所有對它的調用,並確保直接返回該方法所創建的bean,而不是每次都對其進行實際的調用。
比如說,假設你引入了一個其他的CDPlayerbean,它和之前的那個bean完全一樣:
假如對sgtPeppers()的調用就像其他的Java方法調用一樣的話,那麼每個CDPlayer實例都會有一個自己特有的SgtPeppers實例。如果我們討論的是實際的CD播放器和CD光盤的話,這麼做是有意義的。如果你有兩台CD播放器,在物理上並沒有辦法將同一張CD光盤放到兩個CD播放器中。
但是,在軟件領域中,我們完全可以將同一個SgtPeppers實例注入到任意數量的其他bean之中。默認情況下,Spring中的bean都是單例的,我們並沒有必要為第二個CDPlayer bean創建完全相同的SgtPeppers實例。所以,Spring會攔截對sgtPeppers()的調用並確保返回的是Spring所創建的bean,也就是Spring本身在調用sgtPeppers()時所創建的CompactDiscbean。因此,兩個CDPlayer bean會得到相同的SgtPeppers實例。
可以看到,通過調用方法來引用bean的方式有點令人困惑。其實還有一種理解起來更為簡單的方式:
在這裏,cdPlayer()方法請求一個CompactDisc作為參數。當Spring調用cdPlayer()創建CDPlayerbean的時候,它會自動裝配一個CompactDisc到配置方法之中。然後,方法體就可以按照合適的方式來使用它。借助這種技術,cdPlayer()方法也能夠將CompactDisc注入到CDPlayer的構造器中,而且不用明確引用CompactDisc的@Bean方法。
通過這種方式引用其他的bean通常是最佳的選擇,因為它不會要求將CompactDisc聲明到同一個配置類之中。在這裏甚至沒有要求CompactDisc必須要在JavaConfig中聲明,實際上它可以通過組件掃描功能自動發現或者通過XML來進行配置。你可以將配置分散到多個配置類、XML文件以及自動掃描和裝配bean之中,隻要功能完整健全即可。不管CompactDisc是采用什麼方式創建出來的,Spring都會將其傳入到配置方法中,並用來創建CDPlayer bean。
另外,需要提醒的是,我們在這裏使用CDPlayer的構造器實現了DI功能,但是我們完全可以采用其他風格的DI配置。比如說,如果你想通過Setter方法注入CompactDisc的話,那麼代碼看起來應該是這樣的:
再次強調一遍,帶有@Bean注解的方法可以采用任何必要的Java功能來產生bean實例。構造器和Setter方法隻是@Bean方法的兩個簡單樣例。這裏所存在的可能性僅僅受到Java語言的限製。
最後更新:2017-05-31 11:03:35