寫在前面
在前面的文章中,我們知道可以通過多種方式向Spring容器中註冊bean。可以使用@Configuration結合@Bean向Spring容器中註冊bean;可以按照條件向Spring容器中註冊bean;可以使用@Import向容器中快速匯入bean物件;可以在@Import中使用ImportBeanDefinitionRegistrar向容器中註冊bean。
專案工程原始碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotation
FactoryBean概述
一般情況下,Spring通過反射機制利用bean的class屬性指定實現類來例項化bean 。在某些情況下,例項化bean過程比較複雜,如果按照傳統的方式,則需要在
FactoryBean介面對於Spring框架來說佔有重要的地位,Spring 自身就提供了70多個FactoryBean的實現。它們隱藏了例項化一些複雜bean的細節,給上層應用帶來了便利。從Spring 3.0 開始, FactoryBean開始支援泛型,即介面宣告改為FactoryBean
在Spring 5.2.6版本中,FactoryBean介面的定義如下所示。
package org.springframework.beans.factory;
import org.springframework.lang.Nullable;
public interface FactoryBean<T> {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
@Nullable
T getObject() throws Exception;
@Nullable
Class<?> getObjectType();
default boolean isSingleton() {
return true;
}
}
- T getObject():返回由FactoryBean建立的bean例項,如果isSingleton()返回true,則該例項會放到Spring容器中單例項快取池中。
- boolean isSingleton():返回由FactoryBean建立的bean例項的作用域是singleton還是prototype。
- Class
getObjectType():返回FactoryBean建立的bean型別。
這裡,需要注意的是:當配置檔案中
FactoryBean例項
首先,建立一個PersonFactoryBean,實現FactoryBean介面,如下所示。
package io.mykit.spring.plugins.register.bean;
import org.springframework.beans.factory.FactoryBean;
/**
* @author binghe
* @version 1.0.0
* @description 商品的FactoryBean,測試FactoryBean
*/
public class PersonFactoryBean implements FactoryBean<Person> {
//返回一個Person物件,這個物件會被註冊到Spring容器中
@Override
public Person getObject() throws Exception {
return new Person();
}
@Override
public Class<?> getObjectType() {
return Person.class;
}
//bean是否為單例;true:是;false:否
@Override
public boolean isSingleton() {
return true;
}
}
接下來,我們在PersonConfig2類中加入PersonFactoryBean的宣告,如下所示。
@Bean
public PersonFactoryBean personFactoryBean(){
return new PersonFactoryBean();
}
這裡需要小夥伴們注意的是:我在這裡使用@Bean註解向Spring容器中新增的是PersonFactory物件。那我們就來看看Spring容器中有哪些bean。接下來,執行SpringBeanTest類中的testAnnotationConfig7()方法,輸出的結果資訊如下所示。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
personConfig2
io.mykit.spring.plugins.register.bean.Department
io.mykit.spring.plugins.register.bean.Employee
io.mykit.spring.plugins.register.bean.User
io.mykit.spring.plugins.register.bean.Role
person
binghe001
personFactoryBean
company
可以看到,結果資訊中輸出了一個personFactoryBean,我們看下這個personFactoryBean到底是個什麼鬼!此時,我們對SpringBeanTest類中的testAnnotationConfig7()方法稍加改動,新增獲取personFactoryBean的程式碼,並輸出personFactoryBean例項的型別,如下所示。
@Test
public void testAnnotationConfig7(){
ApplicationContext context = new AnnotationConfigApplicationContext(PersonConfig2.class);
String[] names = context.getBeanDefinitionNames();
Arrays.stream(names).forEach(System.out::println);
Object personFactoryBean = context.getBean("personFactoryBean");
System.out.println("personFactoryBean例項的型別為:" + personFactoryBean.getClass());
}
再次執行SpringBeanTest類中的testAnnotationConfig7()方法,輸出的結果資訊如下所示。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
personConfig2
io.mykit.spring.plugins.register.bean.Department
io.mykit.spring.plugins.register.bean.Employee
io.mykit.spring.plugins.register.bean.User
io.mykit.spring.plugins.register.bean.Role
person
binghe001
personFactoryBean
company
personFactoryBean例項的型別為:class io.mykit.spring.plugins.register.bean.Person
可以看到,雖然我在程式碼中使用@Bean註解注入的PersonFactoryBean物件,但是,實際上從Spring容器中獲取到的bean物件卻是呼叫PersonFactoryBean類中的getObject()獲取到的Person物件。
看到這裡,是不是有種豁然開朗的感覺!!!
在PersonFactoryBean類中,我們將Person物件設定為單例項bean,接下來,我們在SpringBeanTest類中的testAnnotationConfig7()方法多次獲取Person物件,並輸出多次獲取的物件是否為同一物件,如下所示。
@Test
public void testAnnotationConfig7(){
ApplicationContext context = new AnnotationConfigApplicationContext(PersonConfig2.class);
String[] names = context.getBeanDefinitionNames();
Arrays.stream(names).forEach(System.out::println);
Object personFactoryBean1 = context.getBean("personFactoryBean");
Object personFactoryBean2 = context.getBean("personFactoryBean");
System.out.println(personFactoryBean1 == personFactoryBean2);
}
執行testAnnotationConfig7()方法輸出的結果資訊如下所示。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
personConfig2
io.mykit.spring.plugins.register.bean.Department
io.mykit.spring.plugins.register.bean.Employee
io.mykit.spring.plugins.register.bean.User
io.mykit.spring.plugins.register.bean.Role
person
binghe001
personFactoryBean
company
true
可以看到,在PersonFactoryBean類的isSingleton()方法中返回true時,每次獲取到的Person物件都是同一個物件,說明Person物件是單例項bean。
這裡,可能就會有小夥伴要問了,如果將Person物件修改成多例項bean呢?別急,這裡我們只需要在PersonFactoryBean類的isSingleton()方法中返回false,即可將Person物件設定為多例項bean,如下所示。
//bean是否為單例;true:是;false:否
@Override
public boolean isSingleton() {
return false;
}
再次執行SpringBeanTest類中的testAnnotationConfig7()方法,輸出的結果資訊如下所示。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
personConfig2
io.mykit.spring.plugins.register.bean.Department
io.mykit.spring.plugins.register.bean.Employee
io.mykit.spring.plugins.register.bean.User
io.mykit.spring.plugins.register.bean.Role
person
binghe001
personFactoryBean
company
false
可以看到,最終結果返回了false,說明此時Person物件是多例項bean。
如何在Spring容器中獲取到FactoryBean物件?
之前,我們使用@Bean註解向Spring容器中註冊的PersonFactoryBean,獲取出來的確實Person物件。那麼,小夥伴們可能會問:我就想獲取PersonFactoryBean例項,該怎麼辦呢?
其實,這也很簡單, 只需要在獲取bean物件時,在id前面加上&符號即可。
開啟我們的測試類SpringBeanTest,在testAnnotationConfig7()方法中新增獲取PersonFactoryBean例項的程式碼,如下所示。
@Test
public void testAnnotationConfig7(){
ApplicationContext context = new AnnotationConfigApplicationContext(PersonConfig2.class);
String[] names = context.getBeanDefinitionNames();
Arrays.stream(names).forEach(System.out::println);
Object personFactoryBean1 = context.getBean("personFactoryBean");
Object personFactoryBean2 = context.getBean("personFactoryBean");
System.out.println("personFactoryBean1型別:" + personFactoryBean1.getClass());
System.out.println("personFactoryBean2型別:" + personFactoryBean2.getClass());
System.out.println(personFactoryBean1 == personFactoryBean2);
Object personFactoryBean3 = context.getBean("&personFactoryBean");
System.out.println("personFactoryBean3型別:" + personFactoryBean3.getClass());
}
執行SpringBeanTest類中的testAnnotationConfig7()方法,輸出的結果資訊如下所示。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
personConfig2
io.mykit.spring.plugins.register.bean.Department
io.mykit.spring.plugins.register.bean.Employee
io.mykit.spring.plugins.register.bean.User
io.mykit.spring.plugins.register.bean.Role
person
binghe001
personFactoryBean
company
personFactoryBean1型別:class io.mykit.spring.plugins.register.bean.Person
personFactoryBean2型別:class io.mykit.spring.plugins.register.bean.Person
false
personFactoryBean3型別:class io.mykit.spring.plugins.register.bean.PersonFactoryBean
可以看到,在獲取bean時,在id前面加上&符號就會獲取到PersonFactoryBean例項物件。
那問題又來了!!為什麼在id前面加上&符號就會獲取到PersonFactoryBean例項物件呢?
接下來,我們就揭開這個神祕的面紗,開啟BeanFactory介面,
package org.springframework.beans.factory;
import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
public interface BeanFactory {
String FACTORY_BEAN_PREFIX = "&";
/**************以下省略n行程式碼***************/
}
看到這裡,是不是明白了呢?沒錯,在BeanFactory介面中定義了一個&字首,只要我們使用bean的id來從Spring容器中獲取bean時,Spring就會知道我們是在獲取FactoryBean本身。
好了,我們們今天就聊到這兒吧!別忘了給個在看和轉發,讓更多的人看到,一起學習一起進步!!
專案工程原始碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotation
寫在最後
如果覺得文章對你有點幫助,請微信搜尋並關注「 冰河技術 」微信公眾號,跟冰河學習Spring註解驅動開發。公眾號回覆“spring註解”關鍵字,領取Spring註解驅動開發核心知識圖,讓Spring註解驅動開發不再迷茫。