生活随笔
收集整理的這篇文章主要介紹了
手写简版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--注入属性和依赖对象 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。