本系列文章介紹ByxContainer的實現思路。java
ByxContainer是一個簡單的輕量級IOC容器,具備如下特性:git
ByxContainer的設計借鑑了ajoo大神的博客。github
本篇文章介紹ByxContainer中與對象初始化有關的設計。ide
在上一篇文章中,咱們解決了對象建立的問題,可是在實際開發中,咱們把一個對象建立出來後,還須要作一些其它的事情來完成對象的初始化:函數
Student s = new Student(); s.setName("XiaoMing"); s.setAge(17); s.setScore(87.5);
在上面的代碼中,使用默認構造函數建立了一個Student
對象,在對象建立出來以後,還設置了Student
的name
、age
、score
這三個屬性。那麼,這種需求要怎麼在ByxContainer中實現呢?ui
若是僅僅使用上一篇文章實現的ConstructorComponent
、StaticFactoryComponent
、InstanceFactoryComponent
,是沒法實現這個需求的,由於這幾個Component
最多隻能表示如何將對象建立出來,可是在對象建立出來以後,咱們還但願對這個對象進行更多的設置,好比調用setter方法設置屬性,或者調用初始化方法等等。this
首先,咱們須要一個東西來封裝對一個對象的操做:設計
public interface Mapper { Object map(Object obj); }
Mapper
接口是一個十分通用的接口,表示轉換操做,它的map
方法接受某個對象,而後返回通過處理後的對象。code
接下來實現一個MapperComponent
:
public class MapperComponent implements Component { private final Component component; private final Mapper mapper; public MapperComponent(Component component, Mapper mapper) { this.component = component; this.mapper = mapper; } @Override public Object create() { return mapper.map(component.create()); } }
MapperComponent
建立時須要傳入一個Component
和Mapper
。MapperComponent
能夠在上一個Component
的基礎上,經過調用Mapper
的map
方法,對上一個Component
生成的對象進行進一步處理。
而後能夠在Component
中實現幾個默認方法:
public interface Component { ... default Component map(Mapper mapper) { return new MapperComponent(this, mapper); } default Component setProperty(String property, Component value) { return this.map(obj -> { // 獲取屬性值 Object v = value.create(); // 調用JavaBean的API將obj的property屬性設置爲v ... }); } default Component invokeSetter(String setter, Component... params) { return this.map(obj -> { // 獲取參數值 Object[] p = Arrays.stream(params).map(Component::create).toArray(); // 反射調用obj的setter方法,並傳遞參數 ... }); } }
把這些經常使用操做封裝成Component
接口的默認方法後,全部的Component
都能以鏈式調用的形式來組合這些操做,詳見下面的使用示例。
setProperty
用於聲明對象屬性的設置,property
是屬性名,value
是用於生成屬性值的組件。注意,這裏value
的類型是Component
而不是Object
,由於屬性值多是一個組件。
invokeSetter
用於聲明調用對象的setter方法,setter
是setter方法名,params
是傳遞的參數,setter方法的參數一樣也能夠是組件,因此params
的類型爲Component[]
。
實際上,設置屬性也是經過調用相應的setter方法實現的,setProperty
與invokeSetter
的區別在於,某些setter方法能夠同時設置兩個屬性,如obj.setNameAndAge("XiaoMing", 17)
;另外,invokeSetter
也能夠用來調用某些初始化方法,如obj.init(...)
。
到這裏,對象初始化的需求就完成得差很少了,本篇文章實現的對象初始化方式結合上一篇文章實現的對象建立一塊兒使用,能夠表達十分多樣的需求。下面給出一些使用示例,以加深理解:
/* Student s = new Student(); s.setId(1001); s.setName("XiaoMing"); s.setAge(17); s.setScore(87.5); */ Component s = constructor(Student.class) .setProperty("name", value("XiaoMing")) .setProperty("age", value(17)) .setProperty("score", value(87.5)); /* Student s = new Student(); s.setNameandAge("XiaoMing", 17); s.setScore(87.5); */ Component s = constructor(Student.class) .invokeSetter("setNameAndAge", value("XiaoMing"), value(17)) .invokeSetter("setScore", value(87.5)); /* StringBuilder builder = new StringBuilder(); builder.append("hello"); builder.append(" world"); String str = builder.toString(); */ Component builder = constructor(StringBuilder.class) .invokeSetter("append", value("hello")) .invokeSetter("append", value(" world")); Component str = instanceFactory(builder, "toString");