08--BeanFactory和FactoryBean的區別
阿新 • • 發佈:2019-02-02
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;
}
}