本文章歡迎轉載,但是轉載請标明出處,程式鋒子https://blog.csdn.net/l13591302862/article/details/111866712
平常對的認識可能不是很深,這次花了點時間去學習了
FactoryBean
,通過本篇文章進行歸納和總結,希望能夠幫到小夥伴們。本人是小白,能力有限,如果有哪個地方描述錯誤,希望有大佬幫忙指出,先在此謝過。
FactoryBean
1 簡介
我們先來談談什麼是
FactoryBean
FactoryBean
是一個接口,實作該接口的類可以自定義建立 bean。
FactoryBean
的實作類本質上還是 bean,經曆 bean 的生命周期,但是通過
FactoryBean
建立的 bean 不經曆 bean 的生命周期。
public interface FactoryBean<T> {
/** 傳回建立的 bean 執行個體 */
T getObject() throws Exception;
/** 傳回 bean 執行個體的類型,類型事先未知則傳回 null */
Class<?> getObjectType();
/** 傳回 true 表示 singleton ,否則為 prototype */
default boolean isSingleton() {
return true;
}
}
ps: 本文使用的的版本為
Spring
5.1.2
那
FactoryBean
一般是用來幹什麼的?
FactoryBean
一般用在架構中,用于建立複雜的 bean 執行個體,例如 SpringAOP 中的
ProxyFactoryBean
用于建立 AOP 代理,Dubbo 中的
ReferenceBean
用于建立遠端服務代理。
2 簡單用法
2.1 預設用法(延遲建立 bean)
準備實體類
public class House {
private String name;
...省略getter和setter...
}
實作
FactoryBean
接口
@Component("house")
public class HouseFactoryBean implements FactoryBean<House> {
private static final String HOUSE_NAME = "CoderFengZi";
@Override
public House getObject() throws Exception {
House house = new House();
house.setName(HOUSE_NAME);
return house;
}
@Override
public Class<?> getObjectType() {
return House.class;
}
}
使用
getBean
方法擷取
bean
執行個體
以下要注意的是,
getBean("beanName")
擷取的是
FactoryBean
建立出來的 bean 執行個體,而
getBean("&beanName")
才是擷取
FactoryBean
本身。
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext context = SpringApplication.run(MyApplication.class, args);
// 擷取FactoryBean建立的bean執行個體
House house = (House) context.getBean("house");
// 擷取FactoryBean本身
FactoryBean<House> factoryBean = (FactoryBean<House>) context.getBean("&house");
// 運作結果:CoderFengZi
System.out.println(house.getName());
// 運作結果:class org.coder.blog.House
System.out.println(factoryBean.getObjectType());
}
}
ps:使用的版本為
SpringBoot
2.4.1
2.2 預設是延遲建立 bean 的,如果我們想進行非延遲建立的話怎麼辦?
- 實作
的子接口FactoryBean
并重寫SmartFactoryBean
方法isEagerInit
- 實作
接口并且在InitializingBean
方法中建立 beanafterPropertiesSet
a. 實作
SmartFactoryBean
的方式
SmartFactoryBean
是
FactoryBean
的子接口,可以重寫
isEagerInit
方法,用來進行非延遲建立 bean 執行個體。我們先看下
SmartFactoryBean
有什麼方法?
public interface SmartFactoryBean<T> extends FactoryBean<T> {
/** 傳回 true 表示 prototype,否則為 singleton */
default boolean isPrototype() {
return false;
}
/** 傳回 true 表示提前建立 bean,否則為延遲建立 */
default boolean isEagerInit() {
return false;
}
}
實作
SmartFactoryBean
接口
@Component("eagerHouse")
public class EagerHouseFactoryBean implements SmartFactoryBean<House> {
private static final String HOUSE_NAME = "CoderFengZi";
@Override
public House getObject() throws Exception {
System.out.println("SmartFactoryBean eager init ...");
House house = new House();
house.setName(HOUSE_NAME);
return house;
}
@Override
public Class<?> getObjectType() {
return House.class;
}
/** 設定成非延遲建立 */
public boolean isEagerInit() {
return true;
}
}
b. 實作
InitializingBean
的方式
該接口的
afterPropertiesSet
方法會在 bean 執行個體化後調用
AbstractAutowireCapableBeanFactory#invokeInitMethods
方法時被調用,這裡不是重點,不做展開,感興趣的朋友可以觀看Spring中的InitializingBean接口的使用
@Component("house")
public class EagerHouseFactoryBean2 implements FactoryBean<House>, InitializingBean {
private static final String HOUSE_NAME = "CoderFengZi";
private volatile House house;
@Override
public House getObject() throws Exception {
if (house == null) {
house = new House();
house.setName(HOUSE_NAME);
}
return house;
}
@Override
public Class<?> getObjectType() {
return House.class;
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean eager init ...");
getObject();
}
}
3 源碼分析
3.1 preInstantiateSingletons
DefaultListableBeanFactory#preInstantiateSingletons
以下的代碼為了友善閱讀做了下精簡,
preInstantiateSingletons
方法是在容器
refresh
方法執行尾聲,用來執行個體化所有的非延遲加載的單例 bean,以下代碼主要解釋
SmartFactoryBean
為何能進行非延遲建立。
public void preInstantiateSingletons() throws BeansException {
...省略...
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// 建立所有非延遲加載的單例bean
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
// 判斷是否是FactoryBean
if (isFactoryBean(beanName)) {
// getBean(beanName)擷取的是FactoryBean建立的bean執行個體
// getBean("&"+beanName)擷取FactoryBean本身
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
final FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
// 判斷是否有代碼運作權限,這裡不做展開
// 感興趣的可以看部落格https://www.jianshu.com/p/dcebd60b4c25
if (System.getSecurityManager() != null
&& factory instanceof SmartFactoryBean) {
...省略...
}
else {
// 預設情況 FactoryBean 延遲建立bean
// 但如果是 SmartFactoryBean,而且設定其 eagerInit 值為 true
// 那麼就進行提前建立
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
// 進行提前建立
getBean(beanName);
}
}
}
else {
getBean(beanName);
}
}
}
...省略...
}
3.2 getBean
由于篇幅有限,我們隻研究
getBean
中與
FactoryBean
有關的代碼,簡單看下調用棧,可以發現最終調用的是
getObject
方法。
我們從
AbstractBeanFactory#getBean
方法開始調用
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
AbstractBeanFactory#doGetBean
,該方法先從三級緩存中擷取
FactoryBean
的對象,關于三級緩存可以看一文告訴你Spring是如何利用“三級緩存“巧妙解決Bean的循環依賴問題的,然後利用
FactoryBean
執行個體進行下一步操作,如果擷取不到執行個體,那麼就會進行bean的執行個體化,我們現在預設
FactoryBean
執行個體本身已經被執行個體化了。
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType,
@Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
// 此處會從三級緩存中擷取bean執行個體,我們預設FactoryBean之前已經被執行個體化了
// 關于三級緩存可以看https://blog.csdn.net/f641385712/article/details/92801300
// 這裡可以擷取到FactoryBean的執行個體
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
...省略(日志記錄)...
else {
...省略(日志記錄)...
}
}
// 通過FactoryBean執行個體來建立bean
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
...省略...
}
AbstractBeanFactory#getObjectForBeanInstance
,這個方法用于擷取 bean 執行個體,先從緩存擷取,如果緩存沒有則建立
protected Object getObjectForBeanInstance(Object beanInstance, String name,
String beanName, @Nullable RootBeanDefinition mbd) {
// 下面這段 if 代碼的意思是:
// 如果不是 FactoryBean,不要使用 & 的名字字首
// 不然會抛出異常
if (BeanFactoryUtils.isFactoryDereference(name)) {
if (beanInstance instanceof NullBean) {
return beanInstance;
}
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
}
if (mbd != null) {
mbd.isFactoryBean = true;
}
return beanInstance;
}
// 非 FactoryBean 直接傳回 bean 執行個體
if (!(beanInstance instanceof FactoryBean)) {
return beanInstance;
}
Object object = null;
if (mbd != null) {
mbd.isFactoryBean = true;
}
// 從緩存 factoryBeanObjectCache 的 map 中擷取執行個體
else {
object = getCachedObjectForFactoryBean(beanName);
}
// 緩存沒有則進行建立
if (object == null) {
// Return bean instance from factory.
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
// Caches object obtained from FactoryBean if it is a singleton.
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
// 判斷是否是Spring的系統類
boolean synthetic = (mbd != null && mbd.isSynthetic());
// 擷取bean執行個體
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
FactoryBeanRegistrySupport#getObjectFromFactoryBean
,這個方法是擷取
FactoryBean
執行個體的核心方法,主要判斷是進行單例建立還是非單例建立,而且建立後運作一些後置處理邏輯,這些邏輯由
FactoryBeanRegistrySupport
的子類實作。
protected Object getObjectFromFactoryBean(FactoryBean<?> factory,
String beanName, boolean shouldPostProcess) {
// 單例建立
if (factory.isSingleton() && containsSingleton(beanName)) {
// 進行加鎖操作
synchronized (getSingletonMutex()) {
// 嘗試從緩存擷取
Object object = this.factoryBeanObjectCache.get(beanName);
// 沒有緩存,進行建立
if (object == null) {
object = doGetObjectFromFactoryBean(factory, beanName);
// 使用雙重校驗機制,再次嘗試從緩存擷取
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
if (alreadyThere != null) {
object = alreadyThere;
}
else {
// 如果不是Spring的内部系統類,即!synthetic
if (shouldPostProcess) {
if (isSingletonCurrentlyInCreation(beanName)) {
// Temporarily return non-post-processed object, not storing it yet..
return object;
}
// 建立單例前進行鎖定,放入singletonsCurrentlyInCreation緩存
beforeSingletonCreation(beanName);
try {
// 可以進行一些後置處理,例如可以進行 AOP 的包裝
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(...);
}
finally {
// 建立單例後解除鎖定,從singletonsCurrentlyInCreation緩存删除
afterSingletonCreation(beanName);
}
}
if (containsSingleton(beanName)) {
this.factoryBeanObjectCache.put(beanName, object);
}
}
}
return object;
}
}
// 非單例建立
else {
Object object = doGetObjectFromFactoryBean(factory, beanName);
if (shouldPostProcess) {
try {
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(...);
}
}
return object;
}
}
FactoryBeanRegistrySupport#doGetObjectFromFactoryBean
,該方法最終調用了
FactoryBean
的
getObject
方法,值得注意的是使用了空對象模式,空對象模式可以看菜鳥教程空對象
private Object doGetObjectFromFactoryBean(FactoryBean<?> factory,
String beanName) throws BeanCreationException {
Object object;
try {
if (System.getSecurityManager() != null) {
AccessControlContext acc = getAccessControlContext();
try {
object = AccessController.doPrivileged(
(PrivilegedExceptionAction<Object>) factory::getObject, acc);
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
// 調用FactoryBean的getObject方法擷取對象
object = factory.getObject();
}
}
catch (FactoryBeanNotInitializedException ex) {
throw new BeanCurrentlyInCreationException(beanName, ex.toString());
}
catch (Throwable ex) {
throw new BeanCreationException(...);
}
// Do not accept a null value for a FactoryBean that's not fully
// initialized yet: Many FactoryBeans just return null then.
// 即時擷取的object為空也不傳回null,而傳回NullBean
// 這裡其實使用了空對象設計模式,可以類比Optional類
// 對空對象設計模式興趣的可以看下面的位址
// 菜鳥教程https://www.runoob.com/design-pattern/null-object-pattern.html
if (object == null) {
if (isSingletonCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(...);
}
object = new NullBean();
}
return object;
}
4 總結
-
的優點是可以自定義建構 bean ,使得建立bean的過程更加靈活。FactoryBean
-
本身可以了解成是一種政策模式,我們需要生成什麼樣的 bean,可以通過實作接口來自定義。這就将建立對象的行為獨立出來了,符合前閉後開的原則,是一種非侵入的方式,達到了解耦的目的FactoryBean
- 我們平常的工作一般可能用不到
,但是了解有這樣一種機制對我們也是一種提升。FactoryBean
如果有興趣可以微信搜一搜 程式鋒子
,關注本人的微信公衆号