自制IOC容器(3)

本系列文章介紹ByxContainer的實現思路。java

ByxContainer是一個簡單的輕量級IOC容器,具備如下特性:git

  • 使用JSON格式的配置文件
  • 支持構造函數注入、靜態工廠注入、實例工廠注入、屬性注入、setter注入、條件注入
  • 組件的延遲加載和單例組件
  • 根據id註冊、獲取容器中的組件

ByxContainer的設計借鑑了ajoo大神的博客github

項目地址:github 碼雲app

本篇文章介紹ByxContainer中與對象初始化有關的設計。ide

回顧

在上一篇文章中,咱們解決了對象建立的問題,可是在實際開發中,咱們把一個對象建立出來後,還須要作一些其它的事情來完成對象的初始化:函數

Student s = new Student();
s.setName("XiaoMing");
s.setAge(17);
s.setScore(87.5);

在上面的代碼中,使用默認構造函數建立了一個Student對象,在對象建立出來以後,還設置了Studentnameagescore這三個屬性。那麼,這種需求要怎麼在ByxContainer中實現呢?ui

Mapper接口和MapperComponent

若是僅僅使用上一篇文章實現的ConstructorComponentStaticFactoryComponentInstanceFactoryComponent,是沒法實現這個需求的,由於這幾個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建立時須要傳入一個ComponentMapperMapperComponent能夠在上一個Component的基礎上,經過調用Mappermap方法,對上一個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方法實現的,setPropertyinvokeSetter的區別在於,某些setter方法能夠同時設置兩個屬性,如obj.setNameAndAge("XiaoMing", 17);另外,invokeSetter也能夠用來調用某些初始化方法,如obj.init(...)

使用ByxContainer

到這裏,對象初始化的需求就完成得差很少了,本篇文章實現的對象初始化方式結合上一篇文章實現的對象建立一塊兒使用,能夠表達十分多樣的需求。下面給出一些使用示例,以加深理解:

/*
    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");
相關文章
相關標籤/搜索