1. 程式人生 > 程式設計 >簡單瞭解Spring中BeanFactory與FactoryBean的區別

簡單瞭解Spring中BeanFactory與FactoryBean的區別

這篇文章主要介紹了簡單瞭解Spring中BeanFactory與FactoryBean的區別,文中通過示例程式碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下

在Spring中有BeanFactory和FactoryBean這2個介面,從名字來看很相似,比較容易搞混。

一、BeanFactory

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

  • DefaultListableBeanFactory
  • XmlBeanFactory
  • ApplicationContext

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

1.1 BenaFactory原始碼

public interface BeanFactory {

  /**
   * 用來獲得例項的引用,並且區分FactoryBean區分。
   * 如果使用bean的名字myJndiObject獲取FactoryBean,返回的是一個工廠,而不是工廠的例項;如果需要獲得工廠例項,需要轉義。
   */
  String FACTORY_BEAN_PREFIX = "&";

  /**
   * 根據bean的名稱,獲取指定的bean例項,該例項可以是共享的,也可以是獨立的.
   */
  Object getBean(String name) throws BeansException;

  /**
   * 根據bean的名稱,獲取指定的bean例項,該例項可以是共享的,也可以是獨立的.並且增加了一個型別的檢驗。
   */
  <T> T getBean(String name,Class<T> requiredType) throws BeansException;

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

  /**
   * 根據給定型別返回匹配的bean例項.
   */
  <T> T getBean(Class<T> requiredType) throws BeansException;

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

  /**
   * 檢查spring的bean容器中是否包含有該bean
   */
  boolean containsBean(String name);

  /**
   * 判斷bean的作用域是否是singleton
   */
  boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

  /**
   * 判斷bena的作用域是否是prototype
   */
  boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

  /**
   * 檢查給定名稱的bean是否和指定型別匹配.更確卻的說是通過檢查給定的bean,返回指定型別的目標物件
   */
  boolean isTypeMatch(String name,ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

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

  /**
   * 獲取給定名稱的bean的class型別
   */
  Class<?> getType(String name) throws NoSuchBeanDefinitionException;

  /**
   * 獲取給定bean名稱的別名,如果根據別名檢索,將會獲得原始bean名稱。
   * 
  String[] getAliases(String name);

}

1.2 使用場景

  • 從Ioc容器中獲取Bean(byName or byType):context.getBean("father",Father.class)、context.getBean("father")
  • 檢索Ioc容器中是否包含指定的Bean: context.containsBean("father")
  • 判斷Bean是否為單例: context.isSingleton("father")

二、FactoryBean

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

2.1 FactoryBean原始碼

public interface FactoryBean<T> {

  /**
   * 從工廠中獲取bean例項
   */
  T getObject() throws Exception;

  /**
   * 從工廠中獲取bean例項物件的型別
   */
  Class<?> getObjectType();

  /**
   * 工廠建立的物件是否是單例
   */
  boolean isSingleton();

}

從它定義的介面可以看出,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 = "通過構造方法初始化例項";
  }

  public MyBean(String message) {
    this.message = message;
  }

  @Override
  public Object getObject() throws Exception {
    // 這裡並不一定要返回MyBean自身的例項,可以是其他任何物件的例項
    return new MyBean("通過FactoryBean.getObject()建立例項");
  }
  @Override
  public Class<?> getObjectType() {
    return MyBean.class;
  }
  public String getMessage() {
    return message;
  }

  @Override
  public boolean isSingleton() {
    return false;
  }
}

MyBean實現了FactoryBean介面的三個方法,getObject()是可以返回任何物件的例項的,這裡測試就返回MyBean自身例項,且返回前給message欄位賦值。同時在構造方法中也為message賦值。然後測試程式碼中先通過名稱獲取Bean例項,列印message的內容,再通過&+名稱獲取例項並列印message內容。

@RunWith(SpringRunner.class)
@SpringBootTest(classes = HelloApplication.class)
public class FactoryBeanTest {
  @Autowired
  private ApplicationContext context;
  @Test
  public void test() {
    MyBean myBean1 = (MyBean) context.getBean("myBean");//返回工廠中的例項,呼叫FactoryBean.getObject()建立例項
    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));
  }
}

列印結果:

myBean1 = 通過FactoryBean.getObject()建立例項
myBean2 = 通過構造方法初始化例項
myBean1.equals(myBean2) = false

2.3 使用場景

說了這麼多,為什麼要有FactoryBean這個東西呢,有什麼具體的作用嗎?

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

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

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

三、區別

  • 他們兩個都是個工廠,但FactoryBean本質上還是一個Bean,也歸BeanFactory管理
  • BeanFactory是Spring容器的頂層介面,FactoryBean更類似於使用者自定義的工廠介面。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。