自制IOC容器(3)

本系列文章介绍ByxContainer的实现思路。

ByxContainer是一个简单的轻量级IOC容器,具有以下特性:

  • 使用JSON格式的配置文件
  • 支持构造函数注入、静态工厂注入、实例工厂注入、属性注入、setter注入、条件注入
  • 组件的延迟加载和单例组件
  • 根据id注册、获取容器中的组件

ByxContainer的设计借鉴了ajoo大神的博客

项目地址:github 码云

本篇文章介绍ByxContainer中与对象初始化有关的设计。

回顾

在上一篇文章中,我们解决了对象创建的问题,但是在实际开发中,我们把一个对象创建出来后,还需要做一些其它的事情来完成对象的初始化:

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

在上面的代码中,使用默认构造函数创建了一个Student对象,在对象创建出来之后,还设置了Studentnameagescore这三个属性。那么,这种需求要怎么在ByxContainer中实现呢?

Mapper接口和MapperComponent

如果仅仅使用上一篇文章实现的ConstructorComponentStaticFactoryComponentInstanceFactoryComponent,是无法实现这个需求的,因为这几个Component最多只能表示如何将对象创建出来,但是在对象创建出来之后,我们还希望对这个对象进行更多的设置,比如调用setter方法设置属性,或者调用初始化方法等等。

首先,我们需要一个东西来封装对一个对象的操作:

public interface Mapper
{
    Object map(Object obj);
}

Mapper接口是一个十分通用的接口,表示转换操作,它的map方法接受某个对象,然后返回经过处理后的对象。

接下来实现一个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");
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

byx2000

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值