自制IOC容器(2)
本系列文章介绍ByxContainer的实现思路。
ByxContainer是一个简单的轻量级IOC容器,具有以下特性:
- 使用JSON格式的配置文件
- 支持构造函数注入、静态工厂注入、实例工厂注入、属性注入、setter注入、条件注入
- 组件的延迟加载和单例组件
- 根据id注册、获取容器中的组件
ByxContainer的设计借鉴了ajoo大神的博客。
本篇文章介绍ByxContainer中与对象创建有关的设计。
对象的创建方式
要解决上面的问题,我们需要思考:创建一个对象到底有多少种方法呢?
在Java中,创建一个对象主要有以下三种方法:
- 构造函数
- 静态工厂
- 实例工厂
最常用的创建对象的方式,肯定是构造函数了:
A a = new A("hello", 123);
静态工厂方式,就是通过调用某个工厂类的静态方法来创建对象,常用于工厂模式:
A a = Factory.create("hello", 123);
实例工厂方式,则是通过调用某个工厂类的实例方法来创建对象:
Factory factory = new Factory();
A a = factory.create("hello", 123);
对于最后一种实例工厂方式,有的人可能觉得很陌生,但是实际上,我们在Java中调用的大多数方法都是实例工厂。其实,凡是通过对象实例调用并且返回一个值的方法都属于实例工厂,即使这个方法与创建对象并没有语义上的关系,如String
的substring
,或者List
的get
。
其实这里还漏了一种方式,那就是:我们不需要容器来帮我们创建对象,而是直接把创建好的对象交给容器,到时候让容器直接返回这个对象就行了。什么时候需要用这种方式呢?比如说,我们想在容器中放一个整数123
,但我们并不希望到时候让容器调用new java.lang.Integer(123)
来创建这个整数,而是希望容器直接返回一个123
给我们。如果这里不理解,可以直接看下面ValueComponent
的实现。
封装通用Component实现类
既然归纳出了上面四种创建对象的方式,那么我们是不是可以对这四种方式分别封装一个通用的Component
实现类呢?这样,假如用户恰好需要使用这四种方式之一来创建对象,就可以直接使用我们写好的实现类,而不用自己编写实现类了。
首先是最简单的ValueComponent
,它封装了已经创建出来的对象:
public class ValueComponent implements Component
{
private final Object value;
public ValueComponent(Object value)
{
this.value = value;
}
@Override
public Object create()
{
return value;
}
}
为了使用更方便,可以在Component
接口定义中添加一个静态方法value
:
public interface Component
{
...
static Component value(Object value)
{
return new ValueComponent(value);
}
}
这样,只要静态导入了Component
,就可以直接写下面的代码:
Component intValue = value(123);
Component stringValue = value("hello");
然后是ConstructorComponent
:
public class ConstructorComponent implements Component
{
private final Class<?> type;
private final Component[] params;
public ConstructorComponent(Class<?> type, Component... params)
{
this.type = type;
this.params = params;
}
@Override
public Object create()
{
// 获取参数
Object[] p = Arrays.stream(params).map(Component::create).toArray();
// 调用type的构造函数,并传递参数
...
}
}
public interface Component
{
...
static Component constructor(Class<?> type, Component... params)
{
return new ConstructorComponent(type, params);
}
}
ConstructorComponent
需要一个type
用来指明调用哪个类的构造函数,params
用来传递构造函数的参数。注意,params
的类型是Component[]
,而不是Object[]
,为什么呢?因为构造函数的参数也可能是一个被IOC容器管理的组件,例如:
B b = new B();
A a = new A(b);
这里a
和b
都是IOC容器中的组件,可以这样来声明这两个组件:
Component b = constructor(B.class);
Component a = constructor(A.class, b);
如果想向构造函数传递常数,可以用ValueComponent
包装一下:
// A a = new A("hello", 123);
Component a = constructor(value("hello"), value(123));
接着是StaticFactoryComponent
:
public class StaticFactoryComponent implements Component
{
private final Class<?> type;
private final String method;
private final Component[] params;
public StaticFactoryComponent(Class<?> type, String method, Component[] params)
{
this.type = type;
this.method = method;
this.params = params;
}
@Override
public Object create()
{
// 获取参数
Object[] p = Arrays.stream(params).map(Component::create).toArray();
// 调用type的静态method方法,并传递参数
...
}
}
public interface Component
{
...
static Component staticFactory(Class<?> type, String method, Component... params)
{
return new StaticFactoryComponent(type, method, params);
}
}
type
是工厂类的Class
,methor
是工厂方法名,params
是方法参数。
最后是InstanceFactoryComponent
:
public class InstanceFactoryComponent implements Component
{
private final Component instance;
private final String method;
private final Component[] params;
public InstanceFactoryComponent(Component instance, String method, Component[] params)
{
this.instance = instance;
this.method = method;
this.params = params;
}
@Override
public Object create()
{
// 获取实例和参数
Object i = instance.create();
Object[] p = Arrays.stream(params).map(Component::create).toArray();
// 调用i的实例method方法,并传递参数
...
}
}
public interface Component
{
...
static Component instanceFactory(Component instance, String method, Component... params)
{
return new InstanceFactoryComponent(instance, method, params);
}
}
instance
是创建实例的组件,method
是实例方法名,params
是方法参数。
使用ByxContainer
到这里,与创建对象有关的操作就完成得差不多了,对于一些常规的创建对象的需求,ByxContainer都能很好地应对。下面给出一些使用示例:
// A a = new A();
Component a = constructor(A.class);
// A a = new A("hello", 123);
Component a = constructor(value("hello"), value(123));
// A a = Factory.createDefault();
Component a = staticFactory(Factory.class, "createDefault");
// A a = Factory.create("hello", 123);
Component a = staticFactory(Factory.class, "create", value("hello"), value(123));
// B b = new B();
// A a = b.create("hello", 123);
Component b = construct(B.class);
Component a = instanceFactory(b, "create", value("hello"), value(123));
// A a = new B().create("hello", 123);
Component a = instanceFactory(constructor(B.class), "create", value("hello"), value(123));