Spring中的BeanFactory與FactoryBean看這一篇就夠了


前言
理解FactoryBean是非常非常有必要的,因為在Spring中FactoryBean最為典型的一個應用就是用來創建AOP的代理對象,不僅如此,而且對理解Mybatis核心源碼也非常有幫助!如果甘願crud,做個快樂的碼農,那我就哦豁豁豁豁豁豁豁豁豁豁豁豁豁豁......
@

BeanFactory和FactoryBean同樣都是spring的接口,名字看起來很相似,但是我覺得要混淆還是很困難的!盡管Spring揭秘一書的作者都喜歡寫上這一句。

請不要混淆BeanFactory 和 FactoryBean。

1、BeanFactory

BeanFactory,以Factory結尾,表示它是一個工廠(接口), 它負責生產和管理bean的一個工廠。在Spring中,BeanFactory是工廠的頂層接口,也是IOC容器的核心接口,因此BeanFactory中定義了管理Bean的通用方法,如 getBeancontainsBean 等,它的職責包括:實例化、定位、配置應用程序中的對象及建立這些對象間的依賴。BeanFactory只是個接口,並不是IOC容器的具體實現,所以Spring容器給出了很多種實現,如 DefaultListableBeanFactoryXmlBeanFactoryApplicationContext等,其中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通過反射機制利用 的class屬性指定實現類實例化Bean。至於為什么會有FactoryBean?原因有兩個:

1、
在某些情況下,實例化Bean過程比較復雜,如果按照傳統的方式,則需要在 中提供大量的配置信息。配置方式的靈活性是受限的,這時采用編碼的方式可能會得到一個簡單的方案。Spring為此提供了一個 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM