自制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");
相关文章
相关标签/搜索