前言
理解FactoryBean是非常非常有必要的,因為在Spring中FactoryBean最為典型的一個應用就是用來創建AOP的代理物件,不僅如此,而且對理解Mybatis核心原始碼也非常有幫助!如果甘愿crud,做個快樂的碼農,那我就哦豁豁豁豁豁豁豁豁豁豁豁豁豁豁......
@
- 1、BeanFactory
- 1.1 BeanFactory 原始碼
- 1.2、BeanFactory 使用場景
- 2、FactoryBean
- 2.1、為什么會有FactoryBean?
- 2.2 、FactoryBean 原始碼
- 2.3 、FactoryBean代碼示例
- 2.4 FactoryBean使用場景
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" >
<property name="dataSource" ref="trade" />
<property name="mapperLocations" value="https://www.cnblogs.com/yichunguo/archive/2020/11/03/classpath*:mapper/trade/*Mapper.xml" />
<property name="configLocation" value="https://www.cnblogs.com/yichunguo/archive/2020/11/03/classpath:mybatis-config.xml" />
<property name="typeAliasesPackage" value="https://www.cnblogs.com/yichunguo/archive/2020/11/03/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
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/201481.html
標籤:其他
上一篇:下戶申請撞單
