生活随笔 
收集整理的這篇文章主要介紹了
                                
手写简版spring --4--注入属性和依赖对象 
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.                        
 
                                
                            一、目標  
首先我們回顧下這幾章節都完成了什么,包括:實現一個容器、定義和注冊Bean、實例化Bean,按照是否包含構造函數實現不同的實例化策略,那么在創建對象實例化這我們還缺少什么?其實還缺少一個關于類中是否有屬性的問題,如果有類中包含屬性那么在實例化的時候就需要把屬性信息填充上,這樣才是一個完整的對象創建。對于屬性的填充不只是 int、Long、String,還包括還沒有實例化的對象屬性,都需要在 Bean 創建時進行填充操作。不過這里我們暫時不會考慮 Bean的循環依賴,否則會把整個功能實現撐大,這樣新人學習時就把握不住了,待后續陸續先把核心功能實現后,再逐步完善。
 
二、設計  
鑒于屬性填充是在 Bean 使用 newInstance 或者 Cglib 創建后,開始補全屬性信息,可以在AbstractAutowireCapableBeanFactory 的 createBean 方法中添加補全屬性方法。
 
 屬性填充要在類實例化創建之后,也就是需要在 AbstractAutowireCapableBeanFactory 的createBean 方法中添加 applyPropertyValues 操作。 由于我們需要在創建Bean時候填充屬性操作,那么就需要在 bean 定義 BeanDefinition 類中,添加 PropertyValues 信息。 另外是填充屬性信息還包括了 Bean 的對象類型,也就是需要再定義一個 BeanReference,里面其實就是一個簡單的 Bean名稱,在具體的實例化操作時進 行遞歸創建和填充,與Spring 源碼實現一樣。Spring 源碼中 BeanReference 是一個接口 三、實現
 
工程結構 類依賴圖  
 
 本章節中需要新增加3 個類,BeanReference(類引用)、PropertyValue(屬性值)、PropertyValues(屬性集合),分別用于類和其他類型屬性填充操作。 另外改動的類主要是AbstractAutowireCapableBeanFactory,在createBean 中補全屬性填充部分。  
 
代碼  
 
public  class  PropertyValue  { private  final  String  name
; private  final  Object  value
; public  PropertyValue ( String  name
,  Object  value
)  { this . name 
=  name
; this . value 
=  value
; } public  String  getName ( )  { return  name
; } public  Object  getValue ( )  { return  value
; } 
} 
 
public  class  PropertyValues  { private  final  List < PropertyValue >  propertyValueList 
=  new  ArrayList < > ( ) ; public  void  addPropertyValue ( PropertyValue  pv
)  { this . propertyValueList
. add ( pv
) ; } public  PropertyValue [ ]  getPropertyValues ( )  { return  this . propertyValueList
. toArray ( new  PropertyValue [ 0 ] ) ; } public  PropertyValue  getPropertyValue ( String  propertyName
)  { for  ( PropertyValue  pv 
:  this . propertyValueList
)  { if  ( pv
. getName ( ) . equals ( propertyName
) )  { return  pv
; } } return  null ; } 
} 
 
 這兩個類的作用就是創建出一個用于傳遞類中屬性信息的類,因為屬性可能會有很多,所以還需要定義一個集合包裝下。 public  class  BeanDefinition  { private  Class  beanClass
; private  PropertyValues  propertyValues
; public  BeanDefinition ( Class  beanClass
)  { this . beanClass 
=  beanClass
; this . propertyValues 
=  new  PropertyValues ( ) ; } public  BeanDefinition ( Class  beanClass
,  PropertyValues  propertyValues
)  { this . beanClass 
=  beanClass
; this . propertyValues 
=  propertyValues 
!=  null  ?  propertyValues 
:  new  PropertyValues ( ) ; } public  Class  getBeanClass ( )  { return  beanClass
; } public  void  setBeanClass ( Class  beanClass
)  { this . beanClass 
=  beanClass
; } public  PropertyValues  getPropertyValues ( )  { return  propertyValues
; } public  void  setPropertyValues ( PropertyValues  propertyValues
)  { this . propertyValues 
=  propertyValues
; } 
} 
 
 在 Bean 注冊的過程中是需要傳遞 Bean 的信息,在幾個前面章節的測試中都有所體現 new BeanDefinition(UserService.class,propertyValues); 所以為了把屬性一定交給 Bean 定義,所以這里填充了 PropertyValues 屬性,同時把兩個構造函數做了一些簡單的優化,避免后面 for 循環時還得判斷屬性填充是否為空。 public  class  BeanReference  { private  final  String  beanName
; public  BeanReference ( String  beanName
)  { this . beanName 
=  beanName
; } public  String  getBeanName ( )  { return  beanName
; } 
} 
 
 
public  abstract  class  AbstractAutowireCapableBeanFactory  extends  AbstractBeanFactory  { 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
) ; }  catch  ( Exception  e
)  { throw  new  BeansException ( "Instantiation of bean failed" ,  e
) ; } addSingleton ( beanName
,  bean
) ; return  bean
; } protected  Object  createBeanInstance ( BeanDefinition  beanDefinition
,  String  beanName
,  Object [ ]  args
)  { Constructor  constructorToUse 
=  null ; Class < ? >  beanClass 
=  beanDefinition
. getBeanClass ( ) ; Constructor < ? > [ ]  declaredConstructors 
=  beanClass
. getDeclaredConstructors ( ) ; for  ( Constructor  ctor 
:  declaredConstructors
)  { if  ( null  !=  args 
&&  ctor
. getParameterTypes ( ) . length 
==  args
. length
)  { constructorToUse 
=  ctor
; break ; } } return  getInstantiationStrategy ( ) . instantiate ( beanDefinition
,  beanName
,  constructorToUse
,  args
) ; } protected  void  applyPropertyValues ( String  beanName
,  Object  bean
,  BeanDefinition  beanDefinition
)  { try  { PropertyValues  propertyValues 
=  beanDefinition
. getPropertyValues ( ) ; for  ( PropertyValue  propertyValue 
:  propertyValues
. getPropertyValues ( ) )  { String  name 
=  propertyValue
. getName ( ) ; Object  value 
=  propertyValue
. getValue ( ) ; if  ( value 
instanceof  BeanReference )  { BeanReference  beanReference 
=  ( BeanReference )  value
; value 
=  getBean ( beanReference
. getBeanName ( ) ) ; } BeanUtil . setFieldValue ( bean
,  name
,  value
) ; } }  catch  ( Exception  e
)  { throw  new  BeansException ( "Error setting property values:"  +  beanName
) ; } } public  InstantiationStrategy  getInstantiationStrategy ( )  { return  instantiationStrategy
; } public  void  setInstantiationStrategy ( InstantiationStrategy  instantiationStrategy
)  { this . instantiationStrategy 
=  instantiationStrategy
; } 
} 
 
 這個類的內容稍微有點長,主要包括三個方法:createBean、createBeanInstance、applyPropertyValues,這里我們主要關注 createBean 的方法中調用的applyPropertyValues 方法。 在 applyPropertyValues 中,通過獲取beanDefinition.getPropertyValues() 循環進行屬性填充操作,如果遇到的是 BeanReference,那么就需要遞歸獲取 Bean 實例,調用 getBean 方法。 當把依賴的 Bean 對象創建完成后,會遞歸回現在屬性填充中。這里需要注意我們并沒有去處理循環依賴的問題,這部分內容較大,后續補充。BeanUtil.setFieldValue(bean, name, value) 是 hutool-all 工具類中的方法,你也可以自己實現  
 
 
public  class  UserDao  { private  static  Map < String ,  String >  hashMap 
=  new  HashMap < > ( ) ; static  { hashMap
. put ( "10001" ,  "小傅哥" ) ; hashMap
. put ( "10002" ,  "八杯水" ) ; hashMap
. put ( "10003" ,  "阿毛" ) ; } public  String  queryUserName ( String  uId
)  { return  hashMap
. get ( uId
) ; } 
} 
 
public  class  UserService  { private  String  uId
; private  UserDao  userDao
; public  void  queryUserInfo ( )  { System . out
. println ( "查詢用戶信息:"  +  userDao
. queryUserName ( uId
) ) ; } public  String  getuId ( )  { return  uId
; } public  void  setuId ( String  uId
)  { this . uId 
=  uId
; } public  UserDao  getUserDao ( )  { return  userDao
; } public  void  setUserDao ( UserDao  userDao
)  { this . userDao 
=  userDao
; } 
} 
 
四、測試  
    @Test public  void  test_BeanFactory ( )  { DefaultListableBeanFactory  beanFactory 
=  new  DefaultListableBeanFactory ( ) ; beanFactory
. registerBeanDefinition ( "userDao" ,  new  BeanDefinition ( UserDao . class ) ) ; PropertyValues  propertyValues 
=  new  PropertyValues ( ) ; propertyValues
. addPropertyValue ( new  PropertyValue ( "uId" ,  "10001" ) ) ; propertyValues
. addPropertyValue ( new  PropertyValue ( "userDao" , new  BeanReference ( "userDao" ) ) ) ; BeanDefinition  beanDefinition 
=  new  BeanDefinition ( UserService . class ,  propertyValues
) ; beanFactory
. registerBeanDefinition ( "userService" ,  beanDefinition
) ; UserService  userService 
=  ( UserService )  beanFactory
. getBean ( "userService" ) ; userService
. queryUserInfo ( ) ; } 
 
 與直接獲取 Bean 對象不同,這次我們還需要先把 userDao 注入到 Bean 容器中beanFactory.registerBeanDefinition("userDao", new BeanDefinition(UserDao.class)); 接下來就是屬性填充的操作了,一種是普通屬性 newPropertyValue(“uId”, “10001”),另外一種是對象屬性new PropertyValue("userDao",new BeanReference("userDao")) 接下來的操作就簡單了,只不過是正常獲取 userService 對象,調用方法即可。  
 
如果注銷UserDao的注冊,則報以下信息
 
 
五、總結  
 在本章節中我們把 AbstractAutowireCapableBeanFactory 類中的創建對象功能又做了擴充,依賴于是否有構造函數的實例化策略完成后,開始補充 Bean 屬性信息。當遇到 Bean 屬性為 Bean 對象時,需要遞歸處理。最后在屬性填充時需要用到反射操作,也可以使用一些工具類處理。 每一個章節的功能點我們都在循序漸進的實現,這樣可以讓新人更好的接受關于Spring 中的設計思路。尤其是在一些已經開發好的類上,怎么擴充新的功能時候的設計更為重要。學習編程有的時候學習思路設計要比僅僅是做簡單實現,更能提升編程思維。 到這一章節關于 Bean 的創建操作就開發完成了,接下來需要整個框架的基礎上完成資源屬性的加載,就是我們需要去動 Xml 配置了,讓我們這小框架越來越像Spring。另外在框架實現的過程中所有的類名都會參考 Spring 源碼,以及相應的設計實現步驟也是與 Spring 源碼中對應,只不過會簡化一些流程,但你可以拿相同的類名,去搜到每一個功能在 Spring 源碼中的實現。 
                            總結 
                            
                                以上是生活随笔 為你收集整理的手写简版spring --4--注入属性和依赖对象 的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                            
                                如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。