前言
理解FactoryBean是非常非常有必要的,因為在Spring中FactoryBean最為典型的一個應用就是用來建立AOP的代理物件,不僅如此,而且對理解Mybatis核心原始碼也非常有幫助!如果甘願crud,做個快樂的碼農,那我就哦豁豁豁豁豁豁豁豁豁豁豁豁豁豁......
@
BeanFactory和FactoryBean同樣都是spring的介面,名字看起來很相似,但是我覺得要混淆還是很困難的!儘管Spring揭祕一書的作者都喜歡寫上這一句。
請不要混淆BeanFactory 和 FactoryBean。
1、BeanFactory
BeanFactory,以Factory結尾,表示它是一個工廠(介面), 它負責生產和管理bean的一個工廠。在Spring中,BeanFactory是工廠的頂層介面,也是IOC容器的核心介面,因此BeanFactory中定義了管理Bean的通用方法,如 getBean 和 containsBean 等,它的職責包括:例項化、定位、配置應用程式中的物件及建立這些物件間的依賴。BeanFactory只是個介面,並不是IOC容器的具體實現,所以Spring容器給出了很多種實現,如 DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等,其中XmlBeanFactory就是常用的一個,該實現將以XML方式描述組成應用的物件及物件間的依賴關係。
1.1 BeanFactory 原始碼
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、BeanFactory 使用場景
1、從Ioc容器中獲取Bean(byName or byType)
2、檢索Ioc容器中是否包含指定的Bean
3、判斷Bean是否為單例
2、FactoryBean
首先FactoryBean是一個Bean,但又不僅僅是一個Bean,這樣聽起來矛盾,但為啥又這樣說呢?其實在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)來進行管理的。但對FactoryBean而言,這個FactoryBean不是簡單的Bean,而是一個能生產或者修飾物件生成的工廠Bean,它的實現與設計模式中的工廠模式和修飾器模式類似
2.1、為什麼會有FactoryBean?
一般情況下,Spring通過反射機制利用
1、
在某些情況下,例項化Bean過程比較複雜,如果按照傳統的方式,則需要在org.springframework.bean.factory.FactoryBean
的工廠類介面,使用者可以通過實現該介面定製例項化Bean的邏輯。FactoryBean介面對於Spring框架來說佔用重要的地位,Spring自身就提供了70多個FactoryBean的實現。它們隱藏了例項化一些複雜Bean的細節,給上層應用帶來了便利。
2、
由於第三方庫不能直接註冊到spring容器,於是可以實現org.springframework.bean.factory.FactoryBean
介面,然後給出自己物件的例項化程式碼即可。
2.2 、FactoryBean 原始碼
public interface FactoryBean<T> {
//從工廠中獲取bean【這個方法是FactoryBean的核心】
@Nullable
T getObject() throws Exception;
//獲取Bean工廠建立的物件的型別【注意這個方法主要作用是:該方法返回的型別是在ioc容器中getbean所匹配的型別】
@Nullable
Class<?> getObjectType();
//Bean工廠建立的物件是否是單例模式
default boolean isSingleton() {
return true;
}
}
方法介紹:
1、
getobject ()方法會返回該FactoryBean “生產” 的物件例項,我們需要實現該方法以給出自己的物件例項化邏輯;
2、
getobjectTYype ()方法僅返回getobject ()方法所返回的物件的型別,如果預先無法確定,則返回null; 特別注意這個方法主要作用是:該方法返回的型別是在ioc容器中getbean所匹配的型別,也就是說ioc中有很多型別的bean,要找到這個bean就是通過getobjectTYype ()方法的返回值型別!好吧,我舉個例子
public class XXX implements FactoryBean {
@Override
public Object getObject() throws Exception {
return new YYY;
}
@Override
public Class<?> getObjectType() { //注意這個方法主要作用是:該方法返回的型別是在ioc容器中getbean所匹配的型別
return AAA.class;
}
}
那麼要想在ioc中找到XXX這個類的bean(實際上是YYY) ,在getbean的時候寫法如下
public class Demo1 {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext =
new AnnotationConfigApplicationContext(Appconfig.class);
annotationConfigApplicationContext.getBean( AAA.class ); // 【注意這裡是AAA.class】
}
}
3、
isSingleton ()方法返回結果用於表明,工廠方法(getobject ()
)所“生產”的物件是否要以singleton形式存在於容器中。如果以singleton形式存在,則返回true,否則返回false;
FactoryBean表現的是一個工廠的職責。 即一個Bean A如果實現了FactoryBean介面,那麼A就變成了一個工廠,根據A的名稱獲取到的實際上是工廠呼叫getObject()返回的物件,而不是A本身,如果要獲取工廠A自身的例項,那麼需要在名稱前面加上'&'符號。 通俗點表達就是
getObject(' name ')返回工廠中的例項
getObject(' &name ')返回工廠本身的例項
通常情況下,bean 無須自己實現工廠模式,Spring 容器擔任了工廠的 角色;但少數情況下,容器中的 bean 本身就是工廠,作用是產生其他 bean 例項。由工廠 bean 產生的其他 bean 例項,不再由 Spring 容器產生,因此與普通 bean 的配置不同,不再需要提供 class 元素。
2.3 、FactoryBean程式碼示例
1、建立一個Appconfig類,掃描com.yichun下的所有子包
@Configuration
@ComponentScan("com.yichun")
public class Appconfig {
}
2、建立一個StudentBean類並實現FactoryBean,並重寫其兩個方法
@Component("studentBean")
public class StudentBean implements FactoryBean {
@Override
public Object getObject() throws Exception {
return new TeacherBean();
}
@Override
public Class<?> getObjectType() { //注意這個方法主要作用是:該方法返回的型別是在ioc容器中getbean所匹配的型別
return StudentBean.class;
}
//一個學生學習方法
public void study(){
System.out.println("學生學習。。。");
}
}
3、再建立一個TeacherBean類
public class TeacherBean {
public void teacher(){
System.out.println("老師教書。。。。");
}
}
4、測試StudentBean型別
public class Demo1 {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Appconfig.class);
StudentBean studentBean = (StudentBean)annotationConfigApplicationContext.getBean("studentBean");
studentBean.study();
}
}
加上“&”符號
public class Demo1 {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Appconfig.class);
//加上了“&”符號
StudentBean studentBean = (StudentBean)annotationConfigApplicationContext.getBean("&studentBean");
studentBean.study();
}
}
執行成功
5、測試一下teacherBean型別
public class Demo1 {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Appconfig.class);
TeacherBean teacherBean = (TeacherBean) annotationConfigApplicationContext.getBean("studentBean");
teacherBean.teacher();
}
}
執行成功
2.4 FactoryBean使用場景
使用場景一:
FactoryBean在Spring中最為典型的一個應用就是用來建立AOP的代理物件。
我們知道AOP實際上是Spring在執行時建立了一個代理物件,也就是說這個物件,是我們在執行時建立的,而不是一開始就定義好的,這很符合工廠方法模式。更形象地說,AOP代理物件通過Java的反射機制,在執行時建立了一個代理物件,在代理物件的目標方法中根據業務要求織入了相應的方法。這個物件在Spring中就是——ProxyFactoryBean。
所以,FactoryBean為我們例項化Bean提供了一個更為靈活的方式,我們可以通過FactoryBean建立出更為複雜的Bean例項。
當然在spring中,Spring容器內部許多地方了使用FactoryBean。下面是一些比較常見的FactoryBean實現:
JndiobjectFactoryBean
LocalSessionFactoryBean
SqlMapClientFactoryBean
ProxyFactoryBean
TransactionProxyFactoryBean
使用場景二:
Mybatis中的SqlSessionFactoryBean
<bean id="tradeSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="trade" />
<property name="mapperLocations" value="classpath*:mapper/trade/*Mapper.xml" />
<property name="configLocation" value="classpath:mybatis-config.xml" />
<property name="typeAliasesPackage" value="com.bytebeats.mybatis3.domain.trade" />
</bean>
package org.mybatis.spring;
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
private static final Log LOGGER = LogFactory.getLog(SqlSessionFactoryBean.class);
......
}
另外提一下,阿里開源的分散式服務框架 Dubbo中的Consumer 也使用到了FactoryBean,
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
">
<!-- 當前應用資訊配置 -->
<dubbo:application name="demo-consumer" />
<!-- 暴露服務協議配置 -->
<dubbo:protocol name="dubbo" port="20813" />
<!-- 暴露服務配置 -->
<dubbo:reference id="demoService" interface="com.alibaba.dubbo.config.spring.api.DemoService" />
</beans>
<dubbo:reference
對應的Bean是com.alibaba.dubbo.config.spring.ReferenceBean
類。
使用場景三:
Hibernate中的SessionFactoryBean。這裡就不再概述。
最後本文難免會有不正之處,歡迎指正評判!歡迎指正評判!歡迎指正評判!!!
參考:
《Spring揭祕》王福強
https://zhuanlan.zhihu.com/p/87382038
https://www.cnblogs.com/aspirant/p/9082858.html