本系列文章介绍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");