一、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.2 使用場景
- 從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));
}
}
myBean1 = 通過FactoryBean.getObject()初始化實例
myBean2 = 通過構造方法初始化實例
myBean1.equals(myBean2) = false
2.3 使用場景
說了這么多,為什么要有FactoryBean
這個東西呢,有什么具體的作用嗎?
FactoryBean在Spring中最為典型的一個應用就是用來創建AOP的代理對象。
我們知道AOP實際上是Spring在運行時創建了一個代理對象,也就是說這個對象,是我們在運行時創建的,而不是一開始就定義好的,這很符合工廠方法模式。更形象地說,AOP代理對象通過Java的反射機制,在運行時創建了一個代理對象,在代理對象的目標方法中根據業務要求織入了相應的方法。這個對象在Spring中就是——ProxyFactoryBean
。
所以,FactoryBean為我們實例化Bean提供了一個更為靈活的方式,我們可以通過FactoryBean創建出更為復雜的Bean實例。
三、區別
- BeanFactory是Spring中IOC容器最核心的接口,遵循了IOC容器中所需的基本接口。例如我們很常見的:ApplicationContext,XmlBeanFactory 等等都使用了BeanFactory這個接口。
- FactoryBean是工廠類接口,當你只是想簡單的去構造Bean,不希望實現原有大量的方法。它是一個Bean,不過這個Bean能夠做為工廠去創建Bean,同時還能修飾對象的生成。
- FactoryBean比BeanFactory在生產Bean的時候靈活,還能修飾對象,帶有工廠模式和裝飾模式的意思在里面,不過它的存在還是以Bean的形式存在。