【Spring】BeanFactory與FactoryBean的區別

1. BeanFactory

BeanFactory是一個接口,它是Spring中工廠的頂層規範,是SpringIoc容器的核心接口,它定義了getBean()、containsBean()等管理Bean的通用方法。Spring的容器都是它的具體實現主要有三種如:

  • DefaultListableBeanFactory
  • XmlBeanFactory
  • ApplicationContext

這些實現類又從不同的維度分別有不同的擴展。

原始的BeanFactory無法支持spring的許多插件,如AOP功能、Web應用等。
ApplicationContext接口,它由BeanFactory接口派生而來,ApplicationContext包含BeanFactory的所有功能,通常建議比BeanFactory優先

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.2 使用場景

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

2. FactoryBean

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

FactoryBean是一個接口,當在IOC容器中的Bean實現了FactoryBean後,通過getBean(String BeanName)獲取到的Bean對象並不是FactoryBean的實現類對象,而是這個實現類中的getObject()方法返回的對象。要想獲取FactoryBean的實現類,就要getBean(&BeanName),在BeanName之前加上&。

一般情況下,Spring通過反射機制利用的class屬性指定實現類實例化Bean,在某些情況下,實例化Bean過程比較複雜,如果按照傳統的方式,則需要在中提供大量的配置信息。配置方式的靈活性是受限的,這時採用編碼的方式可能會得到一個簡單的方案。Spring爲此提供了一個org.springframework.bean.factory.FactoryBean的工廠類接口,用戶可以通過實現該接口定製實例化Bean的邏輯。

FactoryBean接口對於Spring框架來說佔用重要的地位,Spring自身就提供了70多個FactoryBean的實現。它們隱藏了實例化一些複雜Bean的細節,給上層應用帶來了便利。從Spring3.0開始,FactoryBean開始支持泛型,即接口聲明改爲FactoryBean的形式

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接口

package com.siyi;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;

@Component
public class MyBean implements FactoryBean {
    private String message;

    public MyBean() {
        this.message = "通過構造方法初始化實例";
    }

    public MyBean(String message){
        this.message = 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內容。

package com.siyi;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = ApplicationTest.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));
    }
}

2.3 使用場景

說了這麼多,爲什麼要有FactoryBean這個東西呢,有什麼具體的作用嗎?
FactoryBean在Spring中最爲典型的一個應用就是用來創建AOP的代理對象。

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

所以,FactoryBean爲我們實例化Bean提供了一個更爲靈活的方式,我們可以通過FactoryBean創建出更爲複雜的Bean實例。

3. 區別

BeanFactory是提供了IOC容器最基本的形式,給具體的IOC容器的實現提供了規範,FactoryBean可以說爲IOC容器中Bean的實現提供了更加靈活的方式,FactoryBean在IOC容器的基礎上給Bean的實現加上了一個簡單工廠模式和裝飾模式,我們可以在getObject()方法中靈活配置。其實在Spring源碼中有很多FactoryBean的實現類,要想深入準確的理解FactoryBean,只有去讀讀Spring源碼了。

  • 他們兩個都是個工廠,但FactoryBean本質上還是一個Bean,也歸BeanFactory管理
  • BeanFactory是Spring容器的頂層接口,FactoryBean更類似於用戶自定義的工廠接口。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章