在上一篇文章中,我簡單地解釋了 spring 是什麼、bin 是什麼以及上下文。現在是時候嘗試這一切是如何運作的了。 我會在Intellij Idea企業版中自己做。但我的所有範例也應該適用於免費的 Intellij Idea 社群版。如果您在螢幕截圖中看到我有某種您沒有的窗口,請不要擔心,這對於這個專案來說並不重要:) 首先,讓我們建立一個空的 Maven 專案。我在文章中展示瞭如何做到這一點(閱讀直到“是時候將我們的 Maven 項目變成一個 Web 項目了。 ”,之後它已經展示瞭如何創建一個 Web 項目,我們現在不需要這個)讓我們在src/main 資料夾中建立它 /java是一些套件(在我的例子中,我將其稱為“
在左側視窗中,您可以看到項目的結構以及套件和類別
現在我們來看一個更好玩的問題。透過多態性與介面:)讓 我們建立一個介面並建立 7個
ru.javarush.info.fatfaggy.animals
”,您可以將其命名為任何您想要的名稱,只是不要忘記在正確的位置將其替換為您的名稱)。讓我們創建一個類,Main
在其中創建一個方法
public static void main(String[] args) {
...
}
然後打開 pom.xml 檔案並在其中添加一個部分dependencies
。現在我們進入Maven 儲存庫並在那裡找到最新穩定版本的spring 上下文,並將我們獲得的內容貼到 部分中dependencies
。我在本文中更詳細地描述了這個過程(請參閱「在 Maven 中連接依賴項」部分)。然後 Maven 本身會找到並下載必要的依賴項,最後你應該得到類似這樣的東西:
Main
。中間的視窗顯示了我的 pom.xml 的樣子。我還在那裡添加了一個屬性部分,其中我向 Maven 指示了我在原始程式碼中使用的 Java 版本以及要編譯成的版本。這只是為了讓我在啟動時不會收到正在使用舊版 Java 的警告。你可以做到,你不能)在右側視窗中 - 你可以看到,即使我們只連接了 spring 上下文 - 它自動添加了 core、bean、aop 和表達式。可以單獨連接每個模組,在記憶體中為每個模組註冊一個依賴項,並明確指示版本,但目前我們對現在的選項感到滿意。現在讓我們建立一個套件entities
(實體)並在其中建立 3 個類別:Cat
, Dog
, Parrot
。讓每個動物都有一個名字(private String name
,你可以在那裡硬編碼一些值),並且 getter/setter 是公共的。現在轉到類別中Main
並main()
在方法中編寫如下內容:
public static void main(String[] args) {
// create an empty spring context that will search for its beans by annotations in the specified package
ApplicationContext context =
new AnnotationConfigApplicationContext("ru.javarush.info.fatfaggy.animals.entities");
Cat cat = context.getBean(Cat.class);
Dog dog = (Dog) context.getBean("dog");
Parrot parrot = context.getBean("parrot-kesha", Parrot.class);
System.out.println(cat.getName());
System.out.println(dog.getName());
System.out.println(parrot.getName());
}
首先,我們建立一個上下文對象,並在建構函式中為它指定需要掃描是否存在 Bean 的包的名稱。也就是說,Spring將遍歷這個套件並嘗試找到帶有特殊註釋的類,讓Spring知道這是一個bean。之後,它會建立這些類別的物件並將它們放置在其上下文中。之後我們從這個上下文中得到一隻貓。當尋址上下文物件時,我們要求它給我們一個bean(物件),並指出我們需要什麼類別的物件(這裡,順便說一下,你不僅可以指定類,還可以指定介面)。之後Spring將這個類別的物件回傳給我們,我們將其保存到一個變數中。接下來,我們要求 Spring 為我們提供一個名為「dog」的 bean。當Spring建立一個類別物件時,Dog
它會給它一個標準的名稱(如果沒有明確指定所建立的bean的名稱),這是該物件的類別的名稱,只有一個小寫字母。因此,既然我們的類別被稱為Dog
,那麼這樣一個 bean 的名字將是「dog」。如果我們在那裡有一個對象BufferedReader
,那麼 Spring 會給它預設的名稱「bufferedReader」。由於在這種情況下(在 Java 中)無法確切確定這樣的對像是什麼類,因此只需返回某個對象Object
,然後我們手動將其轉換為我們需要的類型Dog
。明確指示類別的選項更方便。好吧,在第三種情況下,我們透過類別和名稱獲得一個 bean。可能只是存在一個情況,在上下文中,一個類別有多個 bean,為了指示我們需要哪個特定 bean,我們指示它的名稱。由於我們這裡也明確標明了班級,所以我們不再需要演員了。 重要的!如果 Spring 根據我們指定的要求找到了多個 bean,它將無法確定給我們哪個 bean,並且會拋出異常。因此,盡量準確地向他表明你需要哪個垃圾箱,這樣就不會出現這種情況。如果 Spring 根據您的條件在其上下文中沒有找到單一 bean,它也會拋出例外。好吧,然後我們只需在螢幕上顯示動物的名稱,以確保這些實際上正是我們需要的物件。但是如果我們現在運行該程序,我們將看到 Spring 發誓它無法在其上下文中找到我們需要的動物。發生這種情況是因為他沒有創造這些豆子。正如我已經說過的,當 Spring 掃描類別時,它會在那裡找到「它的」Spring 註解。如果它沒有找到它,那麼它就不會認為這些類別是他需要創建其 Bean 的類別。@Component
要解決此問題,只需在動物類中的類前面 添加註釋即可。
@Component
public class Cat {
private String name = "Barsik";
...
}
但這還不是全部。如果我們需要明確地向 Spring 指示該類別的 bean 應該有一個特定的名稱,則可以在註解後的括號中指示該名稱。例如,為了讓 Spring 為parrot-kesha
parrot bean 提供我們需要的名稱(main
稍後我們將從中接收到這隻鸚鵡),我們需要執行以下操作:
@Component("parrot-kesha")
public class Parrot {
private String name = "Kesha";
...
}
這就是自動配置的 全部要點。您編寫類,用必要的註釋標記它們,並向 Spring 指示包含您的類的包,Spring 通過該包查找註釋並創建此類類的對象。順便說一句,Spring不僅會搜尋註釋@Component
,還會搜尋從該註釋繼承的所有其他註釋。例如,@Controller
、@RestController
、@Service
和@Repository
其他,我們將在以後的文章中介紹。現在讓我們嘗試做同樣的事情,但使用 java 配置。首先,讓我們@Component
從類別中刪除註解。讓任務變得複雜的是,我們假設這些不是我們自己寫的類,我們可以輕鬆修改、增加一些東西,包括註解。就好像這些類別被打包在某個圖書館裡一樣。在這種情況下,我們無法以任何方式編輯這些類別以使它們被 spring 接受。但我們需要這些類別的物件!這裡我們需要 java 配置來建立這樣的物件。首先,我們建立一個套件(例如)configs
,並在其中建立一個常規 Java 類別(例如),MyConfig
並用註解對其進行標記@Configuration
@Configuration
public class MyConfig {
}
現在我們需要稍微調整main()
在方法中建立上下文的方式。我們可以直接在配置中指定我們的類別:
ApplicationContext context =
new AnnotationConfigApplicationContext(MyConfig.class);
如果我們有幾個不同的類,我們在其中創建 bean,並且我們想要一次連接其中的幾個,我們只需在其中用逗號分隔即可指示它們:
ApplicationContext context =
new AnnotationConfigApplicationContext(MyConfig.class, MyAnotherConfig.class);
好吧,如果我們有太多,並且我們想一次連接它們,我們只需在此處指出包含它們的包的名稱:
ApplicationContext context =
new AnnotationConfigApplicationContext("ru.javarush.info.fatfaggy.animals.configs");
在這種情況下,Spring將遍歷這個套件並找到所有帶有註釋標記的類別@Configuration
。好吧,如果我們有一個非常大的程序,其中配置被分為不同的包,我們只需用逗號分隔配置來指示包的名稱:
ApplicationContext context =
new AnnotationConfigApplicationContext("ru.javarush.info.fatfaggy.animals.database.configs",
"ru.javarush.info.fatfaggy.animals.root.configs",
"ru.javarush.info.fatfaggy.animals.web.configs");
好吧,或者是對它們來說更常見的套件的名稱:
ApplicationContext context =
new AnnotationConfigApplicationContext("ru.javarush.info.fatfaggy.animals");
您可以按照自己的意願進行操作,但在我看來,第一個選項(只需指定帶有配置的類別)最適合我們的程式。在建立上下文時,Spring會搜尋那些被註解標記的類@Configuration
,並在自身中建立這些類別的物件。之後它將嘗試呼叫這些類別中標有註解的方法@Bean
,這意味著這些方法將傳回它已經放置在其上下文中的bean(物件)。好吧,現在讓我們使用 java 配置在我們的類別中創建 cat、dog 和 parrot beans。這很簡單:
@Bean
public Cat getCat() {
return new Cat();
}
事實證明,我們自己手動創建了貓並將其交給 Spring,而他已經將我們的這個物件放入了他的上下文中。由於我們沒有明確指定 bean 的名稱,Spring 將為 bean 指定與方法名稱相同的名稱。在我們的例子中,貓的豆子的名字是「getCat
」。但由於在main
-e 中我們仍然不是透過名稱而是透過類別來獲取貓,所以在這種情況下,這個 bin 的名稱對我們來說並不重要。以同樣的方式用狗創建一個 bean,但請記住 Spring 會透過方法的名稱來命名這樣的 bean。要使用 parrot 明確命名我們的 bean,只需在註解後的括號中指明其名稱@Bean
:
@Bean("parrot-kesha")
public Object weNeedMoreParrots() {
return new Parrot();
}
正如您所看到的,這裡我指示了返回值的類型Object
,並將該方法稱為任何名稱。這不會以任何方式影響 bean 的名稱,因為我們在此處明確地設定了它。但最好不要突然指出傳回值類型和方法名稱,但或多或少要清楚一些。即使是為了你自己,當你在一年後打開這個專案時。:) 現在讓我們考慮這樣一個情況:在建立一個 bean 時我們需要使用另一個 bean。例如,我們希望cat bean中貓的名字由鸚鵡的名字和字串「-killer」組成。沒問題!
@Bean
public Cat getCat(Parrot parrot) {
Cat cat = new Cat();
cat.setName(parrot.getName() + "-killer");
return cat;
}
這裡 Spring 會看到,在建立這個 bean 之前,他需要將已經建立的 parrot bean 轉移到這裡。因此,他會建立一個對我們方法的呼叫鏈,以便先呼叫創建鸚鵡的方法,然後將這隻鸚鵡傳遞給創建貓的方法。這就是依賴注入運作的地方:Spring 本身將所需的 parrot bean 傳遞給我們的方法。如果想法抱怨變量parrot
,請不要忘記將創建鸚鵡的方法中的返回類型從更改Object
為Parrot
。此外,Java 配置可讓您在建立 bean 的方法中執行絕對任何 Java 程式碼。你真的可以做任何事情:創建其他輔助對象,調用任何其他方法,甚至那些沒有用 spring 註釋標記的方法,make 循環,條件 - 無論你想到什麼!所有這些都無法使用自動配置來實現,更不用說使用 xml 配置了。
WeekDay
實作該介面的類別:Monday
、、、、、、、、。讓我們在介面中建立一個方法,該方法將傳回對應類別的星期幾的名稱。也就是說,該類別將返回“ ”等。假設啟動應用程式時的任務是將一個 bean 放置在與目前星期相對應的上下文中。並非所有類別的所有 bean 都實作了該接口,而只是我們需要的那個。可以這樣做: Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
String getWeekDayName()
Monday
monday
WeekDay
@Bean
public WeekDay getDay() {
DayOfWeek dayOfWeek = LocalDate.now().getDayOfWeek();
switch (dayOfWeek) {
case MONDAY: return new Monday();
case TUESDAY: return new Tuesday();
case WEDNESDAY: return new Wednesday();
case THURSDAY: return new Thursday();
case FRIDAY: return new Friday();
case SATURDAY: return new Saturday();
default: return new Sunday();
}
}
這裡的返回值類型是我們的接口,該方法根據當前星期幾返回接口實現類別的真實對象。現在在方法中main()
我們可以這樣做:
WeekDay weekDay = context.getBean(WeekDay.class);
System.out.println("It's " + weekDay.getWeekDayName() + " today!");
它告訴我今天是星期日:)我確信如果我明天運行該程序,上下文中將出現一個完全不同的物件。請注意,這裡我們只是透過介面獲取bean:context.getBean(WeekDay.class)
。Spring 將在其上下文中查看哪個 bean 實作了此類介面並傳回它。好吧,事實證明,WeekDay
類型 的變數中有一個類型的對象Sunday
,而我們大家都已經熟悉的多態性是在使用該變數時開始的。:) 關於組合方法的一些話,其中一些 bean 是由 Spring 本身創建的,使用掃描包來查找帶有註釋的類別的存在@Component
,而其他一些 bean 是使用 java 配置創建的。為此,讓我們回到原始版本,當時類別Cat
、Dog
和 都Parrot
用註解進行了標記@Component
。假設我們想在春天之前使用自動掃描包裹的方式為我們的動物創建垃圾箱entities
,但像我們剛才那樣創建一個包含星期幾的垃圾箱。您需要做的就是在類別層級添加,我們在第一個註解MyConfig
中建立上下文時指定,並在括號中指示需要掃描的套件以及自動建立的必要類別的bean: main
@ComponentScan
@Configuration
@ComponentScan("ru.javarush.info.fatfaggy.animals.entities")
public class MyConfig {
@Bean
public WeekDay getDay() {
DayOfWeek dayOfWeek = LocalDate.now().getDayOfWeek();
switch (dayOfWeek) {
case MONDAY: return new Monday();
case TUESDAY: return new Tuesday();
case WEDNESDAY: return new Wednesday();
case THURSDAY: return new Thursday();
case FRIDAY: return new Friday();
case SATURDAY: return new Saturday();
default: return new Sunday();
}
}
}
事實證明,在創建上下文時,Spring 發現它需要處理該類別MyConfig
。他進入其中,發現他需要掃描套件「ru.javarush.info.fatfaggy.animals.entities
」並創建這些類別的 bean,之後他執行getDay()
該類別中的方法MyConfig
並將類型 bean 添加WeekDay
到他的上下文中。在這個方法中,main()
我們現在可以存取我們需要的所有 bean:動物物件和帶有星期幾的 bean。如何確保 Spring 也取得一些 xml 設定 - 如有必要,請自行在 Internet 上進行 google :) 摘要:
- 嘗試使用自動配置;
- 在自動設定過程中,我們指定包含需要建立bean的類別的套件的名稱;
- 這些類別標有註釋
@Component;
- spring 遍歷所有此類並創建它們的物件並將它們放置在上下文中;
- 如果因為某些原因自動配置不適合我們,我們使用java配置;
- 在這種情況下,我們創建一個常規 Java 類,其方法將返回我們需要的對象,並用註釋標記這樣的類,
@Configuration
以防我們在創建上下文時掃描整個包而不是透過配置指定特定類; - 此類傳回 bean 的方法會以註解進行標記
@Bean
;
@ComponentScan
。
GO TO FULL VERSION