1. 程式人生 > >08--BeanFactory和FactoryBean的區別

08--BeanFactory和FactoryBean的區別

BeanFactory和FactoryBean是兩個容易混淆的概念,很多人喜歡問兩者之間的區別,其實兩者之間並無內在聯絡。

  • BeanFactory介面:IoC容器的頂級介面,是IoC容器的最基礎實現,也是訪問Spring容器的根介面,負責對bean的建立,訪問等工作。

  • FactoryBean介面:可以返回bean的例項的工廠bean,通過實現該介面可以對bean進行一些額外的操作,例如根據不同的配置型別返回不同型別的bean,簡化xml配置等。在使用上也有些特殊,BeanFactory介面中有一個字元常量String FACTORY_BEAN_PREFIX = "&"; 當我們去獲取BeanFactory型別的bean時,如果beanName不加&則獲取到對應bean的例項;如果beanName加上&,則獲取到BeanFactory本身的例項;FactoryBean介面對應Spring框架來說佔有重要的地位,Spring本身就提供了70多個FactoryBean的實現。他們隱藏了例項化一些複雜的細節,給上層應用帶來了便利。從Spring3.0開始,FactoryBean開始支援泛型。

下面來看FactoryBean的使用方式

1.簡化xml配置,隱藏細節

使用Setter方法注入大量屬性會造成配置檔案臃腫,這時可以考慮使用FactoryBean來簡化配置。

  • bean
package com.lyc.cn.v2.day01.factoryBean;

/**
 * @author: LiYanChao
 * @create: 2018-09-05 11:50
 */
public class Student {
	/** 姓名 */
	private String name;
	/** 年齡 */
	private int age;
	/** 班級名稱 */
	private String className;
// ...可能能會有更多的屬性 public Student() { } public Student(String name, int age, String className) { this.name = name; this.age = age; this.className = className; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() {
return age; } public void setAge(int age) { this.age = age; } public String getClassName() { return className; } public void setClassName(String className) { this.className = className; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", className='" + className + '\'' + '}'; } }
package com.lyc.cn.v2.day01.factoryBean;

import org.springframework.beans.factory.FactoryBean;

/**
 * @author: LiYanChao
 * @create: 2018-09-05 11:49
 */
public class StudentFactoryBean implements FactoryBean<Student> {

	private String studentInfo;

	@Override
	public Student getObject() throws Exception {
		if (this.studentInfo == null) {
			throw new IllegalArgumentException("'studentInfo' is required");
		}

		// 分割屬性
		String[] splitStudentInfo = studentInfo.split(",");
		if (null == splitStudentInfo || splitStudentInfo.length != 3) {
			throw new IllegalArgumentException("'studentInfo' config error");
		}

		// 建立Student並填充屬性
		Student student = new Student();
		student.setName(splitStudentInfo[0]);
		student.setAge(Integer.valueOf(splitStudentInfo[1]));
		student.setClassName(splitStudentInfo[2]);
		return student;
	}

	@Override
	public Class<?> getObjectType() {
		return StudentFactoryBean.class;
	}

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

	public void setStudentInfo(String studentInfo) {
		this.studentInfo = studentInfo;
	}
}

Student是一個普通的類,StudentFactoryBean實現了FactoryBean介面,是一個FactoryBean。

  • xml
<bean id="student" class="com.lyc.cn.v2.day01.factoryBean.StudentFactoryBean" p:studentInfo="張三,25,三年二班"/>
  • 測試
//FactoryBean簡化配置測試
System.out.println(xmlBeanFactory.getBean("student"));
System.out.println(xmlBeanFactory.getBean("&student"));
  • 結果
========測試方法開始=======

Student{name='張三', age=25, className='三年二班'}
com.lyc.cn.v2.day01.factoryBean.StudentFactoryBean@1ff8b8f

========測試方法結束=======
  • 分析
    xmlBeanFactory.getBean("student") 獲取到的是StudentFactoryBean產生的例項,也就是Student類的例項;而xmlBeanFactory.getBean("&student")獲取到的是StudentFactoryBean自己的例項。
2.返回不同Bean的例項
  • bean
package com.lyc.cn.v2.day01.factoryBean;

/**
 * 傢俱介面
 */
public interface Furniture {
	void sayHello();
}

package com.lyc.cn.v2.day01.factoryBean;

/**
 * 椅子
 * @author: LiYanChao
 * @create: 2018-09-30 17:35
 */
public class Chair implements Furniture{

	@Override
	public void sayHello() {
		System.out.println("我是一把椅子。");
	}
}

package com.lyc.cn.v2.day01.factoryBean;

/**
 * 桌子
 * @author: LiYanChao
 * @create: 2018-09-30 17:35
 */
public class Desk implements Furniture{

	@Override
	public void sayHello() {
		System.out.println("我是一個桌子。");
	}
}

package com.lyc.cn.v2.day01.factoryBean;

import org.springframework.beans.factory.FactoryBean;

/**
 * 傢俱工廠bean
 * @author: LiYanChao
 * @create: 2018-09-05 15:11
 */
public class FurnitureFactoryBean implements FactoryBean<Furniture> {

	private String furniture;

	@Override
	public Furniture getObject() throws Exception {
		if (null == furniture) {
			throw new IllegalArgumentException("'furniture' is required");
		}
		if ("chair".equals(furniture)) {
			return new Chair();
		} else if ("desk".equals(furniture)) {
			return new Desk();
		} else {
			throw new IllegalArgumentException("'furniture' type error");
		}
	}

	@Override
	public Class<?> getObjectType() {
		if (null == furniture) {
			throw new IllegalArgumentException("'furniture' is required");
		}
		if ("chair".equals(furniture)) {
			return Chair.class;
		} else if ("desk".equals(furniture)) {
			return Desk.class;
		} else {
			throw new IllegalArgumentException("'furniture' type error");
		}
	}

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

	public void setFurniture(String furniture) {
		this.furniture = furniture;
	}
}
  • xml
<bean id="furniture" class="com.lyc.cn.v2.day01.factoryBean.FurnitureFactoryBean" p:furniture="desk"/>
  • 測試
@Test
public void test12() {
	//FactoryBean簡單工廠測試
	Furniture furniture = xmlBeanFactory.getBean("furniture", Furniture.class);
	furniture.sayHello();
}
  • 結果
========測試方法開始=======

我是一個桌子。

========測試方法結束=======
  • 說明
    新建了傢俱接介面和桌子、椅子實現類,通過xml檔案配置,在FurnitureFactoryBean的getObject方法進行判斷,並返回不同的傢俱型別例項。
3.FactoryBean原始碼

該介面的原始碼比較少,只聲明瞭三個介面

public interface FactoryBean<T> {

	//返回此工廠管理的物件的例項(可能Singleton和Prototype)
	//如果此FactoryBean在呼叫時尚未完全初始化(例如,因為它涉及迴圈引用),則丟擲相應的FactoryBeanNotInitializedException。
    //從Spring 2.0開始,允許FactoryBeans返回null 物件。工廠會將此視為正常使用價值; 在這種情況下,它不再丟擲FactoryBeanNotInitializedException。
    //鼓勵FactoryBean實現現在自己丟擲FactoryBeanNotInitializedException,視情況而定。
	T getObject() throws Exception;

	//返回此FactoryBean建立的物件型別,預設返回null
	Class<?> getObjectType();

    //例項是否單例模式,預設返回true
	default boolean isSingleton() {
		return true;
	}

}