生活随笔
收集整理的這篇文章主要介紹了
手写简版spring --9--对象作用域和FactoryBean
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一、目標
交給 Spring 管理的 Bean 對象,一定就是我們用類創建出來的 Bean 嗎?創建出來的 Bean 就永遠是單例的嗎,沒有可能是原型模式嗎?在集合 Spring 框架下,我們使用的 MyBatis 框架中,它的核心作用是可以滿足用戶不需要實現 Dao 接口類,就可以通過 xml 或者注解配置的方式完成對數據庫執行 CRUD 操作,那么在實現這樣的 ORM 框架中,是怎么把一個數據庫操作的 Bean 對象交給 Spring 管理的呢。因為我們在使用 Spring、MyBatis 框架的時候都可以知道,并沒有手動的去創建任何操作數據庫的 Bean 對象,有的僅僅是一個接口定義,而這個接口定義竟然可以被注入到其他需要使用 Dao 的屬性中去了,那么這一過程最核心待解決的問題,就是需要完成把復雜且以代理方式動態變化的對象,注冊到 Spring 容器中。而為了滿足這樣的一個擴展組件開發的需求,就需要我們在現有手寫的 Spring 框架中,添加這一能力。
二、方案
關于提供一個能讓使用者定義復雜的 Bean 對象,功能點非常不錯,意義也非常大,因為這樣做了之后 Spring 的生態種子孵化箱就此提供了,誰家的框架都可以在此標準上完成自己服務的接入。但這樣的功能邏輯設計上并不復雜,因為整個 Spring 框架在開發的過程中就已經提供了各項擴展能力的接茬,你只需要在合適的位置提供一個接茬的處理接口調用和相應的功能邏輯實現即可,像這里的目標實現就是對外提供一個可以二次從 FactoryBean 的 getObject 方法中獲取對象的功能即可,這樣所有實現此接口的對象類,就可以擴充自己的對象功能了。MyBatis 就是實現了一個 MapperFactoryBean 類,在 getObject 方法中提供 SqlSession 對執行 CRUD 方法的操作 整體設計結構如下圖:
整個的實現過程包括了兩部分,一個解決單例還是原型對象,另外一個處理 FactoryBean 類型對象創建過程中關于獲取具體調用對象的getObject 操作。 SCOPE_SINGLETON、SCOPE_PROTOTYPE,對象類型的創建獲取方式,主要區分在于 AbstractAutowireCapableBeanFactory#createBean 創建完成對象后是否放入到內存中,如果不放入則每次獲取都會重新創建。 createBean執行對象創建、屬性填充、依賴加載、前置后置處理、初始化等操作后,就要開始做執行判斷整個對象是否是一個 FactoryBean對象,如果是這樣的對象,就需要再繼續執行獲取 FactoryBean 具體對象中的 getObject 對象了。整個 getBean過程中都會新增一個單例類型的判斷factory.isSingleton(),用于決定是否使用內存存放對象信息。
四、實現
工程結構
small
- spring
- step
- 09
└── src├── main│ └── java│ └── cn
. bugstack
. springframework│ ├── beans│ │ ├── factory│ │ │ ├── config│ │ │ │ ├──
AutowireCapableBeanFactory . java│ │ │ │ ├──
BeanDefinition . java│ │ │ │ ├──
BeanFactoryPostProcessor . java│ │ │ │ ├──
BeanPostProcessor . java│ │ │ │ ├──
BeanReference . java│ │ │ │ ├──
ConfigurableBeanFactory . java│ │ │ │ └──
SingletonBeanRegistry . java│ │ │ ├── support│ │ │ │ ├──
AbstractAutowireCapableBeanFactory . java│ │ │ │ ├──
AbstractBeanDefinitionReader . java│ │ │ │ ├──
AbstractBeanFactory . java│ │ │ │ ├──
BeanDefinitionReader . java│ │ │ │ ├──
BeanDefinitionRegistry . java│ │ │ │ ├──
CglibSubclassingInstantiationStrategy . java│ │ │ │ ├──
DefaultListableBeanFactory . java│ │ │ │ ├──
DefaultSingletonBeanRegistry . java│ │ │ │ ├──
DisposableBeanAdapter . java│ │ │ │ ├──
FactoryBeanRegistrySupport . java│ │ │ │ ├──
InstantiationStrategy . java│ │ │ │ └──
SimpleInstantiationStrategy . java │ │ │ ├── support│ │ │ │ └──
XmlBeanDefinitionReader . java│ │ │ ├──
Aware . java│ │ │ ├──
BeanClassLoaderAware . java│ │ │ ├──
BeanFactory . java│ │ │ ├──
BeanFactoryAware . java│ │ │ ├──
BeanNameAware . java│ │ │ ├──
ConfigurableListableBeanFactory . java│ │ │ ├──
DisposableBean . java│ │ │ ├──
FactoryBean . java│ │ │ ├──
HierarchicalBeanFactory . java│ │ │ ├──
InitializingBean . java│ │ │ └──
ListableBeanFactory . java│ │ ├──
BeansException . java│ │ ├──
PropertyValue . java│ │ └──
PropertyValues . java │ ├── context│ │ ├── support│ │ │ ├──
AbstractApplicationContext . java │ │ │ ├──
AbstractRefreshableApplicationContext . java │ │ │ ├──
AbstractXmlApplicationContext . java │ │ │ ├──
ApplicationContextAwareProcessor . java │ │ │ └──
ClassPathXmlApplicationContext . java │ │ ├──
ApplicationContext . java │ │ ├──
ApplicationContextAware . java │ │ └──
ConfigurableApplicationContext . java│ ├── core
. io│ │ ├──
ClassPathResource . java │ │ ├──
DefaultResourceLoader . java │ │ ├──
FileSystemResource . java │ │ ├──
Resource . java │ │ ├──
ResourceLoader . java │ │ └──
UrlResource . java│ └── utils│ └──
ClassUtils . java└── test└── java└── cn
. bugstack
. springframework
. test├── bean│ ├──
UserDao . java│ └──
UserService . java└──
ApiTest . java
Spring 單例、原型以及 FactoryBean 功能實現類關系,如圖
以上整個類關系圖展示的就是添加 Bean 的實例化是單例還是原型模式以及 FactoryBean 的實現。 其實整個實現的過程并不復雜,只是在現有的 AbstractAutowireCapableBeanFactory 類以及繼承的抽象類 AbstractBeanFactory 中進行擴展。 不過這次我們把 AbstractBeanFactory 繼承的DefaultSingletonBeanRegistry 類,中間加了一層 FactoryBeanRegistrySupport,這個類在Spring 框架中主要是處理關于 FactoryBean 注冊的支撐操作。
Bean的作用范圍定義和xml解析
public class BeanDefinition { String SCOPE_SINGLETON
= ConfigurableBeanFactory . SCOPE_SINGLETON
; String SCOPE_PROTOTYPE
= ConfigurableBeanFactory . SCOPE_PROTOTYPE
; private Class beanClass
; private PropertyValues propertyValues
; private String initMethodName
; private String destroyMethodName
; private String scope
= SCOPE_SINGLETON
; private boolean singleton
= true ; private boolean prototype
= false ;
}
singleton、prototype,是本次在 BeanDefinition 類中新增加的兩個屬性信息,用于把從 spring.xml中解析到的 Bean 對象作用范圍填充到屬性中。
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { protected void doLoadBeanDefinitions ( InputStream inputStream
) throws ClassNotFoundException { for ( int i
= 0 ; i
< childNodes
. getLength ( ) ; i
++ ) { if ( ! ( childNodes
. item ( i
) instanceof Element ) ) continue ; if ( ! "bean" . equals ( childNodes
. item ( i
) . getNodeName ( ) ) ) continue ; Element bean
= ( Element ) childNodes
. item ( i
) ; String id
= bean
. getAttribute ( "id" ) ; String name
= bean
. getAttribute ( "name" ) ; String className
= bean
. getAttribute ( "class" ) ; String initMethod
= bean
. getAttribute ( "init-method" ) ; String destroyMethodName
= bean
. getAttribute ( "destroy-method" ) ; String beanScope
= bean
. getAttribute ( "scope" ) ; Class < ? > clazz
= Class . forName ( className
) ; String beanName
= StrUtil . isNotEmpty ( id
) ? id
: name
; if ( StrUtil . isEmpty ( beanName
) ) { beanName
= StrUtil . lowerFirst ( clazz
. getSimpleName ( ) ) ; } BeanDefinition beanDefinition
= new BeanDefinition ( clazz
) ; beanDefinition
. setInitMethodName ( initMethod
) ; beanDefinition
. setDestroyMethodName ( destroyMethodName
) ; if ( StrUtil . isNotEmpty ( beanScope
) ) { beanDefinition
. setScope ( beanScope
) ; } getRegistry ( ) . registerBeanDefinition ( beanName
, beanDefinition
) ; } }
}
在解析 XML 處理類 XmlBeanDefinitionReader 中,新增加了關于 Bean 對象配置中 scope 的解析,并把這個屬性信息填充到 Bean 定義中。beanDefinition.setScope(beanScope)
創建和修改對象時候判斷單例和原型模式
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory { private InstantiationStrategy instantiationStrategy
= new CglibSubclassingInstantiationStrategy ( ) ; @Override protected Object createBean ( String beanName
, BeanDefinition beanDefinition
, Object [ ] args
) throws BeansException { Object bean
= null ; try { bean
= createBeanInstance ( beanDefinition
, beanName
, args
) ; applyPropertyValues ( beanName
, bean
, beanDefinition
) ; bean
= initializeBean ( beanName
, bean
, beanDefinition
) ; } catch ( Exception e
) { throw new BeansException ( "Instantiation of bean failed" , e
) ; } registerDisposableBeanIfNecessary ( beanName
, bean
, beanDefinition
) ; if ( beanDefinition
. isSingleton ( ) ) { addSingleton ( beanName
, bean
) ; } return bean
; } protected void registerDisposableBeanIfNecessary ( String beanName
, Object bean
, BeanDefinition beanDefinition
) { if ( ! beanDefinition
. isSingleton ( ) ) return if ( bean
instanceof DisposableBean || StrUtil . isNotEmpty ( beanDefinition
. getDestroyMethodName ( ) ) ) { registerDisposableBean ( beanName
, new DisposableBeanAdapter ( bean
, beanName
, beanDefinition
) ) ; } }
}
單例模式和原型模式的區別就在于是否存放到內存中,如果是原型模式那么就不會存放到內存中,每次獲取都重新創建對象,另外非 Singleton類型的 Bean 不需要執行銷毀方法。 所以這里的代碼會有兩處修改,一處是 createBean 中判斷是否添加到addSingleton(beanName, bean);,另外一處是 registerDisposableBeanIfNecessary銷毀注冊中的判斷 if (!beanDefinition.isSingleton()) return;。
定義 FactoryBean 接口
public interface FactoryBean < T > { T getObject ( ) throws Exception ; Class < ? > getObjectType ( ) ; boolean isSingleton ( ) ;
}
FactoryBean 中需要提供3個方法,獲取對象、對象類型,以及是否是單例對象,如果是單例對象依然會被放到內存中。
實現一個 FactoryBean 注冊服務
public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry { private final Map < String , Object > factoryBeanObjectCache
= new ConcurrentHashMap < String , Object > ( ) ; protected Object getCachedObjectForFactoryBean ( String beanName
) { Object object
= this . factoryBeanObjectCache
. get ( beanName
) ; return ( object
!= NULL_OBJECT
? object
: null ) ; } protected Object getObjectFromFactoryBean ( FactoryBean factory
, String beanName
) { if ( factory
. isSingleton ( ) ) { Object object
= this . factoryBeanObjectCache
. get ( beanName
) ; if ( object
== null ) { object
= doGetObjectFromFactoryBean ( factory
, beanName
) ; this . factoryBeanObjectCache
. put ( beanName
, ( object
!= null ? object
: NULL_OBJECT
) ) ; } return ( object
!= NULL_OBJECT
? object
: null ) ; } else { return doGetObjectFromFactoryBean ( factory
, beanName
) ; } } private Object doGetObjectFromFactoryBean ( final FactoryBean factory
, final String beanName
) { try { return factory
. getObject ( ) ; } catch ( Exception e
) { throw new BeansException ( "FactoryBean threw exception on object[" + beanName
+ "] creation" , e
) ; } } }
FactoryBeanRegistrySupport 類主要處理的就是關于 FactoryBean此類對象的注冊操作,之所以放到這樣一個單獨的類里,就是希望做到不同領域模塊下只負責各自需要完成的功能,避免因為擴展導致類膨脹到難以維護。 同樣這里也定義了緩存操作factoryBeanObjectCache,用于存放單例類型的對象,避免重復創建。在日常使用用,基本也都是創建的單例對象 doGetObjectFromFactoryBean 是具體的獲取 FactoryBean#getObject()方法,因為既有緩存的處理也有對象的獲取,所以額外提供了 getObjectFromFactoryBean進行邏輯包裝,這部分的操作方式是不和你日常做的業務邏輯開發非常相似。從Redis取數據,如果為空就從數據庫獲取并寫入Redis
擴展 AbstractBeanFactory 創建對象邏輯
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory { protected < T > T doGetBean ( final String name
, final Object [ ] args
) { Object sharedInstance
= getSingleton ( name
) ; if ( sharedInstance
!= null ) { return ( T ) getObjectForBeanInstance ( sharedInstance
, name
) ; } BeanDefinition beanDefinition
= getBeanDefinition ( name
) ; Object bean
= createBean ( name
, beanDefinition
, args
) ; return ( T ) getObjectForBeanInstance ( bean
, name
) ; } private Object getObjectForBeanInstance ( Object beanInstance
, String beanName
) { if ( ! ( beanInstance
instanceof FactoryBean ) ) { return beanInstance
; } Object object
= getCachedObjectForFactoryBean ( beanName
) ; if ( object
== null ) { FactoryBean < ? > factoryBean
= ( FactoryBean < ? > ) beanInstance
; object
= getObjectFromFactoryBean ( factoryBean
, beanName
) ; } return object
; }
}
首先這里把 AbstractBeanFactory 原來繼承的 DefaultSingletonBeanRegistry,修改為繼承 FactoryBeanRegistrySupport。因為需要擴展出創建 FactoryBean對象的能力,所以這就想一個鏈條服務上,截出一個段來處理額外的服務,并把鏈條再鏈接上。 此處新增加的功能主要是在 doGetBean方法中,添加了調用 (T) getObjectForBeanInstance(sharedInstance, name) 對獲取 FactoryBean 的操作。 在 getObjectForBeanInstance 方法中做具體的 instanceof判斷,另外還會從 FactoryBean 的緩存中獲取對象,如果不存在則調用FactoryBeanRegistrySupport#getObjectFromFactoryBean,執行具體的操作。
四、測試
事先準備
public interface IUserDao { String queryUserName ( String uId
) ;
}
這個章節我們刪掉 UserDao,定義一個 IUserDao 接口,之所這樣做是為了通過 FactoryBean做一個自定義對象的代理操作。
public class UserService { private String uId
; private String company
; private String location
; private IUserDao userDao
; public String queryUserInfo ( ) { return userDao
. queryUserName ( uId
) + "," + company
+ "," + location
; }
}
在 UserService 新修改了一個原有 UserDao 屬性為 IUserDao,后面我們會給這個屬性注入代理對象。
定義 FactoryBean 對象
public class ProxyBeanFactory implements FactoryBean < IUserDao > { @Override public IUserDao getObject ( ) throws Exception { InvocationHandler handler
= ( proxy
, method
, args
) -> { Map < String , String > hashMap
= new HashMap < > ( ) ; hashMap
. put ( "10001" , "小傅哥" ) ; hashMap
. put ( "10002" , "八杯水" ) ; hashMap
. put ( "10003" , "阿毛" ) ; return "你被代理了 " + method
. getName ( ) + ":" + hashMap
. get ( args
[ 0 ] . toString ( ) ) ; } ; return ( IUserDao ) Proxy . newProxyInstance ( Thread . currentThread ( ) . getContextClassLoader ( ) , new Class [ ] { IUserDao . class } , handler
) ; } @Override public Class < ? > getObjectType ( ) { return IUserDao . class ; } @Override public boolean isSingleton ( ) { return true ; }
}
這是一個實現接口 FactoryBean 的代理類 ProxyBeanFactory 名稱,主要是模擬了 UserDao 的原有功能,類似于 MyBatis 框架中的代理操作。 getObject() 中提供的就是一個 InvocationHandler的代理對象,當有方法調用的時候,則執行代理對象的功能。
配置文件
<?xml version="1.0" encoding="UTF-8"?>
< beans> < bean id = " userService" class = " cn.bugstack.springframework.test.bean.UserService" scope = " prototype" > < property name = " uId" value = " 10001" /> < property name = " company" value = " 騰訊" /> < property name = " location" value = " 深圳" /> < property name = " userDao" ref = " proxyUserDao" /> </ bean> < bean id = " proxyUserDao" class = " cn.bugstack.springframework.test.bean.ProxyBeanFactory" />
</ beans>
在配置文件中,我們把 proxyUserDao 這個代理對象,注入到 userService 的 userDao 中。與上一章節相比,去掉了 UserDao 實現類,轉而用代理類替換
單元測試(單例&原型)
@Test
public void test_prototype ( ) { ClassPathXmlApplicationContext applicationContext
= new ClassPathXmlApplicationContext ( "classpath:spring.xml" ) ; applicationContext
. registerShutdownHook ( ) ; UserService userService01
= applicationContext
. getBean ( "userService" , UserService . class ) ; UserService userService02
= applicationContext
. getBean ( "userService" , UserService . class ) ; System . out
. println ( userService01
) ; System . out
. println ( userService02
) ; System . out
. println ( userService01
+ " 十六進制哈希:" + Integer . toHexString ( userService01
. hashCode ( ) ) ) ; System . out
. println ( ClassLayout . parseInstance ( userService01
) . toPrintable ( ) ) ;
}
在 spring.xml 配置文件中,設置了 scope=”prototype” 這樣就每次獲取到的對象都應該是一個新的對象。 這里判斷對象是否為一個會看到打印的類對象的哈希值,所以我們把十六進制哈希打印出來。
單元測試(代理對象)
@Test
public void test_factory_bean ( ) { ClassPathXmlApplicationContext applicationContext
= new ClassPathXmlApplicationContext ( "classpath:spring.xml" ) ; applicationContext
. registerShutdownHook ( ) ; UserService userService
= applicationContext
. getBean ( "userService" , UserService . class ) ; System . out
. println ( "測試結果:" + userService
. queryUserInfo ( ) ) ;
}
關于 FactoryBean 的調用并沒有太多不一樣,因為所有的不同都已經被 spring.xml 配置進去了。當然你可以直接調用 spring.xml 配置的對象
從測試結果來看,我們的代理類 ProxyBeanFactory 已經完美替換掉了 UserDao 的功能。 雖然看上去這一點實現并不復雜,甚至有點簡單。但就是這樣一點點核心內容的設計了,解決了所有需要和 Spring結合的其他框架交互鏈接問題。如果對此類內容感興趣,也可以閱讀小傅哥《中間件設計和開發》
五、總結
在 Spring框架整個開發的過程中,前期的各個功能接口類擴展的像膨脹了似的,但到后期在完善功能時,就沒有那么難了,反而深入理解后會覺得功能的補充,都比較簡單。只需要再所遇領域范圍內進行擴展相應的服務實現即可。 當你仔細閱讀完關于 FactoryBean 的實現以及測試過程的使用,以后再需要使用 FactoryBean開發相應的組件時候,一定會非常清楚它是如何創建自己的復雜 Bean 對象以及在什么時候初始化和調用的。遇到問題也可以快速的排查、定位和解決。 如果你在學習的過程中感覺這些類、接口、實現、繼承,穿梭的很復雜,一時半會腦子還反應不過來。那么你最好的方式是動手去畫畫這些類關系圖,梳理下實現的結構,看看每個類在干什么??粗荒苁侵?#xff0c;動手才能學會!
總結
以上是生活随笔 為你收集整理的手写简版spring --9--对象作用域和FactoryBean 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。