天天看點

Spring中的 IoC詳解,BeanFactory 與 FactoryBean差別

一、BeanFactory

BeanFactory

是一個接口,它是Spring中工廠的頂層規範,是SpringIoc容器的核心接口,它定義了

getBean()

containsBean()

等管理Bean的通用方法。Spring的容器都是它的具體實作如:

  • DefaultListableBeanFactory
  • XmlBeanFactory
  • ApplicationContext

這些實作類又從不同的次元分别有不同的擴充。

1.1、源碼

public interface BeanFactory {

	//對FactoryBean的轉義定義,因為如果使用bean的名字檢索FactoryBean得到的對象是工廠生成的對象,
	//如果需要得到工廠本身,需要轉義
	String FACTORY_BEAN_PREFIX = "&";

	//根據bean的名字,擷取在IOC容器中得到bean執行個體
	Object getBean(String name) throws BeansException;

	//根據bean的名字和Class類型來得到bean執行個體,增加了類型安全驗證機制。
	<T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;

	Object getBean(String name, Object... args) throws BeansException;

	<T> T getBean(Class<T> requiredType) throws BeansException;

	<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

	//提供對bean的檢索,看看是否在IOC容器有這個名字的bean
	boolean containsBean(String name);

	//根據bean名字得到bean執行個體,并同時判斷這個bean是不是單例
	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

	boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

	boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

	//得到bean執行個體的Class類型
	@Nullable
	Class<?> getType(String name) throws NoSuchBeanDefinitionException;

	//得到bean的别名,如果根據别名檢索,那麼其原名也會被檢索出來
	String[] getAliases(String name);
}
           

1.1、使用場景

  • 從Ioc容器中擷取Bean(byName or byType)
  • 檢索Ioc容器中是否包含指定的Bean
  • 判斷Bean是否為單例

二、FactoryBean

首先它是一個Bean,但又不僅僅是一個Bean。它是一個能生産或修飾對象生成的工廠Bean,類似于設計模式中的工廠模式和裝飾器模式。它能在需要的時候生産一個對象,且不僅僅限于它自身,它能傳回任何Bean的執行個體。

2.1、源碼

public interface FactoryBean<T> {

	//從工廠中擷取bean
	@Nullable
	T getObject() throws Exception;

	//擷取Bean工廠建立的對象的類型
	@Nullable
	Class<?> getObjectType();

	//Bean工廠建立的對象是否是單例模式
	default boolean isSingleton() {
		return true;
	}
}
           

從它定義的接口可以看出,

FactoryBean

表現的是一個工廠的職責。 即一個Bean A如果實作了FactoryBean接口,那麼A就變成了一個工廠,根據A的名稱擷取到的實際上是工廠調用

getObject()

傳回的對象,而不是A本身,如果要擷取工廠A自身的執行個體,那麼需要在名稱前面加上'

&

'符号。

  • getObject('name')傳回工廠中的執行個體
  • getObject('&name')傳回工廠本身的執行個體

通常情況下,bean 無須自己實作工廠模式,Spring 容器擔任了工廠的 角色;但少數情況下,容器中的 bean 本身就是工廠,作用是産生其他 bean 執行個體。由工廠 bean 産生的其他 bean 執行個體,不再由 Spring 容器産生,是以與普通 bean 的配置不同,不再需要提供 class 元素。

2.2、示例

先定義一個Bean實作FactoryBean接口

@Component
public class MyBean implements FactoryBean {
    private String message;
    public MyBean() {
        this.message = "通過構造方法初始化執行個體";
    }
    @Override
    public Object getObject() throws Exception {
        // 這裡并不一定要傳回MyBean自身的執行個體,可以是其他任何對象的執行個體。
        //如return new Student()...
        return new MyBean("通過FactoryBean.getObject()建立執行個體");
    }
    @Override
    public Class<?> getObjectType() {
        return MyBean.class;
    }
    public String getMessage() {
        return message;
    }
}
           

MyBean實作了FactoryBean接口的兩個方法,getObject()是可以傳回任何對象的執行個體的,這裡測試就傳回MyBean自身執行個體,且傳回前給message字段指派。同時在構造方法中也為message指派。然後測試代碼中先通過名稱擷取Bean執行個體,列印message的内容,再通過

&+名稱

擷取執行個體并列印message内容。

@RunWith(SpringRunner.class)
@SpringBootTest(classes = TestApplication.class)
public class FactoryBeanTest {
    @Autowired
    private ApplicationContext context;
    @Test
    public void test() {
        MyBean myBean1 = (MyBean) context.getBean("myBean");
        System.out.println("myBean1 = " + myBean1.getMessage());
        MyBean myBean2 = (MyBean) context.getBean("&myBean");
        System.out.println("myBean2 = " + myBean2.getMessage());
        System.out.println("myBean1.equals(myBean2) = " + myBean1.equals(myBean2));
    }
}


----console

myBean1 = 通過FactoryBean.getObject()初始化執行個體
myBean2 = 通過構造方法初始化執行個體
myBean1.equals(myBean2) = false
           

注:對這段代碼的了解,IOC将該工廠類注入到了BeanFactory中,所謂的控制反轉,即類的執行個體化不需要類自己通過構造去new一個出來,通過參數注入的方式給需要使用該對象的類使用。getBean("myBean")擷取到的是由Spring IOC容器建立出來的工廠執行個體,内部調用了接口getObject()傳回的結果,而在加了&符号後,則是由該工廠類自己去執行個體化出來的對象。

2.3、使用場景

說了這麼多,為什麼要有

FactoryBean

這個東西呢,有什麼具體的作用嗎?

FactoryBean在Spring中最為典型的一個應用就是用來建立AOP的代理對象。

我們知道AOP實際上是Spring在運作時建立了一個代理對象,也就是說這個對象,是我們在運作時建立的,而不是一開始就定義好的,這很符合工廠方法模式。更形象地說,AOP代理對象通過Java的反射機制,在運作時建立了一個代理對象,在代理對象的目标方法中根據業務要求織入了相應的方法。這個對象在Spring中就是——

ProxyFactoryBean

是以,FactoryBean為我們執行個體化Bean提供了一個更為靈活的方式,我們可以通過FactoryBean建立出更為複雜的Bean執行個體。

三、差別

  • 他們兩個都是個工廠,但

    FactoryBean

    本質上還是一個Bean,也歸

    BeanFactory

    管理
  • BeanFactory

    是Spring容器的頂層接口,

    FactoryBean

    更類似于使用者自定義的工廠接口。
總結

BeanFactory

FactoryBean

的差別确實容易混淆,死記硬背是不行的,最好還是從源碼層面,置于spring的環境中去了解。