【String註解驅動開發】如何按照條件向Spring容器中註冊bean?這次我懂了!!
阿新 • • 發佈:2020-06-10
## 寫在前面
> 當bean是單例項,並且沒有設定懶載入時,Spring容器啟動時,就會例項化bean,並將bean註冊到IOC容器中,以後每次從IOC容器中獲取bean時,直接返回IOC容器中的bean,不再建立新的bean。
>
> 如果bean是單例項,並且使用@Lazy註解設定了懶載入,則Spring容器啟動時,不會例項化bean,也不會將bean註冊到IOC容器中,只有第一次獲取bean的時候,才會例項化bean,並且將bean註冊到IOC容器中。
>
> 如果bean是多例項,則Spring容器啟動時,不會例項化bean,也不會將bean註冊到IOC容器中,以後每次從IOC容器中獲取bean時,都會建立一個新的bean返回。
>
> Spring支援按照條件向IOC容器中註冊bean,滿足條件的bean就會被註冊到IOC容器中,不滿足條件的bean就不會被註冊到IOC容器中。接下來,我們就一起來探討Spring中如何實現按照條件向IOC容器中註冊bean。
>
> 專案工程原始碼已經提交到GitHub:[https://github.com/sunshinelyz/spring-annotation](https://github.com/sunshinelyz/spring-annotation)
## @Conditional註解概述
@Conditional註解可以按照一定的條件進行判斷,滿足條件向容器中註冊bean,不滿足條件就不向容器中註冊bean。
@Conditional註解是由 SpringFramework 提供的一個註解,位於 org.springframework.context.annotation 包內,定義如下。
```java
package org.springframework.context.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
Class[] value();
}
```
從@Conditional註解的原始碼來看,@Conditional註解可以新增到類上,也可以新增到方法上。在@Conditional註解中,存在一個Condition型別或者其子型別的Class物件陣列,Condition是個啥?我們點進去看一下。
```java
package org.springframework.context.annotation;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.core.type.AnnotatedTypeMetadata;
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
```
可以看到,Condition是一個函式式介面,對於函式式介面不瞭解的同學可以參見【Java8新特性】中的《[【Java8新特性】還沒搞懂函式式介面?趕快過來看看吧!](https://mp.weixin.qq.com/s?__biz=Mzg3MzE1NTIzNA==&mid=2247484537&idx=1&sn=e83c8394f8e4ae5408edea37590ce68c&chksm=cee51bb4f99292a2529ee9eccbd6190df8e228b91383519bb0aacf7f9b16375f0e4a3c5143c8&token=2141222822&lang=zh_CN#rd)》一文。也可以直接檢視《[Java8新特性專欄](https://mp.weixin.qq.com/mp/appmsgalbum?action=getalbum&__biz=Mzg3MzE1NTIzNA==&scene=1&album_id=1325066823947321344&uin=MjY0ODI3NDMxNQ%3D%3D&key=6898a2b07ab8315b346e49cb946636ac30bf791060f59e1b3f9f4ea45fb4da4877bfadf96f172ddd5c6b08a41f048552cb5f0bd0ce35428c3b965b4ad02581553b0146795ea47d06c637b6ac15c1aa2f&devicetype=Windows+10+x64&version=62090070&lang=zh_CN&ascene=1&pass_ticket=qJSd3kgjDozsrmjeER7cQ2NthBP7CDbXMWtwnybrvKPZeqAFnyLdEnCu4KuRgr28)》來系統學習Java8的新特性。
所以,我們使用@Conditional註解時,需要一個類實現Spring提供的Condition介面,它會匹配@Conditional所符合的方法,然後我們可以使用我們在@Conditional註解中定義的類來檢查。
**@Conditional註解的使用場景如下所示。**
- **可以作為類級別的註解直接或者間接的與@Component相關聯,包括@Configuration類;**
- **可以作為元註解,用於自動編寫構造性註解;**
- **作為方法級別的註解,作用在任何@Bean方法上。**
## 向Spring容器註冊bean
### 不帶條件註冊bean
我們在PersonConfig2類中新增person01()方法和person02()方法,併為兩個方法新增@Bean註解,如下所示。
```java
@Bean("binghe001")
public Person person01(){
return new Person("binghe001", 18);
}
@Bean("binghe002")
public Person person02(){
return new Person("binghe002", 20);
}
```
那麼,這兩個bean預設是否會被註冊到Spring容器中呢,我們新建一個測試用例來測試一下。在SpringBeanTest類中新建testAnnotationConfig6()方法,如下所示。
```java
@Test
public void testAnnotationConfig6(){
ApplicationContext context = new AnnotationConfigApplicationContext(PersonConfig2.class);
String[] names = context.getBeanNamesForType(Person.class);
Arrays.stream(names).forEach(System.out::println);
}
```
我們執行testAnnotationConfig6()方法,輸出的結果資訊如下所示。
```bash
person
binghe001
binghe002
```
從輸出結果可以看出,同時輸出了binghe001和binghe002。說明預設情況下,Spring容器會將單例項並且非懶載入的bean註冊到IOC容器中。
接下來,我們再輸出bean的名稱和bean例項物件資訊,此時我們在testAnnotationConfig6()方法中新增相應的程式碼片段,如下所示。
```java
@Test
public void testAnnotationConfig6(){
ApplicationContext context = new AnnotationConfigApplicationContext(PersonConfig2.class);
String[] names = context.getBeanNamesForType(Person.class);
Arrays.stream(names).forEach(System.out::println);
Map