ByxContainer是一个用Java编写的轻量级IOC容器,具备以下个性:
- 应用JSON格局的配置文件
- 反对构造函数注入、动态工厂注入、实例工厂注入、属性注入、setter注入、条件注入
- 组件的提早加载和单例组件
- 依据id注册、获取容器中的组件
我的项目地址:https://github.com/byx2000/By...
配置文件
ByxContainer应用JSON作为配置文件的格局。你能够将配置文件命名为任何名字,放在任何你喜爱的门路下。
ByxContainer配置文件的根本框架如下:
{ "typeAlias": { // 配置components中应用的类型别名 }, "components": { // 定义容器中的所有组件 }}
键 | 类型 | 阐明 | 是否必须 |
---|---|---|---|
typeAlias | 对象 | components 中应用的类型别名 | 否 |
components | 对象 | 定义容器中的所有组件 | 是 |
对于类型别名的应用,请参考这里。
组件
ByxContainer应用组件来管理系统中的所有对象。组件代表了零碎中的一个对象,并封装了该对象的创立过程。每个组件都有一个惟一的key,当咱们向ByxContainer注册一个组件时,须要为组件指定一个惟一的key,同时还要定义组件的创立过程和依赖关系。
ByxContainer的所有组件都定义在components
中。组件以键值对的模式写在components
对象中,键就是组件的id,值就是组件的定义。
有很多种形式来定义组件,详见下文。
{ "components": { "c1": ... // 组件c1的定义 "c2": ... // 组件c2的定义 "c3": ... // 组件c3的定义 ... }}
加载与应用ByxContainer
在应用程序的初始化代码中,依照以下形式加载配置文件,并初始化容器:
InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("配置文件门路");ContainerFactory factory = new JsonContainerFactory(inputStream);Container container = factory.create();
容器初始化实现后,须要应用注册组件时的id来获取某个特定组件:
// 获取id为c1的组件SomeType c1 = container.getComponent("c1");
组件的创立形式
在ByxContainer中,反对以下四种组件创立形式:
- 常数
- 构造函数(constructor)
- 动态工厂(static factory)
- 实例工厂(instance factor)
常数
ByxContainer反对JSON中的所有根本类型常数,包含整数、浮点数、字符串、布尔、null,它们在Java中对应的类型别离是:int
、double
、String
、boolean
、null
。
{ "components": { "intValue": 123, "doubleValue": 3.14, "stringValue": "hello", "trueValue": true, "falseValue": false, "nullValue": null }}
以上配置申明了几种不同类型的常数组件。
构造函数
配置构造函数创立形式时,须要指定全限定类名,如果要传递参数则指定参数数组。
键 | 类型 | 阐明 | 是否必须 |
---|---|---|---|
class | 字符串 | 全限定类名 | 是 |
parameter | 数组 | 结构函数参数 | 否 |
{ "components": { "a1": { "class": "byx.test.A" }, "a2": { "class": "byx.test.A", "parameters": ["hello", 123] } }}
以上配置等价于以下Java代码:
A a1 = new A();A a2 = new A("hello", 123);
注:parameters
数组中的元素既能够是常数,也能够嵌套其它组件的定义:
{ "components": { "a3": { "class": "byx.test.A", "parameters": [ "hello", 123, {"class": "byx.test.B"} ] } }}
以上配置等价于以下Java代码:
A a3 = new A("hello", 123, new B());
动态工厂
配置动态工厂创立形式时,须要指定工厂类的类名和工厂函数名,如果要传递参数则指定参数数组。
键 | 类型 | 阐明 | 是否必须 |
---|---|---|---|
factory | 字符串 | 工厂类全限定类名 | 是 |
method | 字符串 | 工厂类静态方法名 | 是 |
parameter | 数组 | 办法参数 | 否 |
{ "components": { "a1": { "factory": "byx.test.Factory", "method": "createDefault" }, "a2": { "factory": "byx.test.Factory", "method": "create", "parameters": ["hello", 123] } }}
以上配置等价于以下Java代码:
A a1 = Factory.createDefault();A a2 = Factory.create("hello", 123);
实例工厂
配置实例工厂创立形式时,须要指定实例组件和工厂函数名,如果要传递参数则指定参数数组。
键 | 类型 | 阐明 | 是否必须 |
---|---|---|---|
instance | 对象或根本类型 | 实例组件定义 | 是 |
method | 字符串 | 实例工厂办法名 | 是 |
parameter | 数组 | 办法参数 | 否 |
{ "components": { "a1": { "instance": {"class": "byx.test.B"}, "method": "createDefault" }, "a2": { "instance": {"class": "byx.test.B"}, "method": "create", "parameters": ["hello", 123] } }}
以上配置等价于以下Java代码:
A a1 = new B().createDefault();A a2 = new B().create("hello", 123);
组件的依赖申明
申明完组件的创立形式后,还可持续申明组件的依赖。ByxContainer反对以下两种依赖申明形式:
- 属性设置
- setter办法
属性设置
ByxContainer反对在组件创立实现之后对组件的属性进行设置。这里的属性等同于JavaBean的Property。
键 | 类型 | 阐明 | 是否必须 |
---|---|---|---|
properties | 对象 | (属性名, 属性值)键值对 | 否 |
{ "components": { "a": { "class": "byx.test.A", "properties": { "id": 1001, "name": "byx", "score": 97.5 } } }}
以上配置等价于以下Java代码:
A a = new A();a.setId(1001);a.setName("byx");a.setScore(97.5);
注:properties
中的属性值既能够是常数,也能够嵌套其它组件的定义:
{ "components": { "a": { "class": "byx.test.A", "properties": { "id": 1001, "b": {"class": "byx.test.B"} } } }}
以上配置等价于以下Java代码:
A a = new A();a.setId(1001);a.setB(new B());
setter办法
ByxContainer反对在组件创立实现之后调用组件的setter办法,并传递参数。
键 | 类型 | 阐明 | 是否必须 |
---|---|---|---|
setters | 对象 | (setter办法名, 办法参数)键值对 | 否 |
{ "components": { "a": { "class": "byx.test.A", "setters": { "setId": [1001], "setNameAndScore": ["byx", 97.5] } } }}
以上配置等价于以下Java代码:
A a = new A();a.setId(1001);a.setNameAndScore("byx", 97.5);
其它组件定义
ByxContainer还反对以下非凡组件定义:
- 汇合组件
- 援用组件
- 条件组件
汇合组件
ByxContainer反对List
、Set
和Map
这三种汇合组件。
上面是这三种汇合组件的定义:
{ "components": { "c1": {"list": [1, 2, 3]}, "c2": {"set": [4, 5, 6]}, "c3": { "map": { "k1": 100, "k2": 200, "k3": 300 } }, "c4": { "map": [ {"key": 400, "value": "v1"}, {"key": 500, "value": "v2"}, {"key": 600, "value": "v3"} ] } }}
以上配置等价于以下Java代码:
List<Object> c1 = List.of(1, 2, 3);Set<Object> c2 = Set.of(4, 5, 6);Map<Object> c3 = Map.of("k1", 100, "k2", 200, "k3", 300);Map<Object> c4 = Map.of(400, "v1", 500, "v2", 600, "v3");
注:
- 汇合组件的元素既能够是常数,也能够嵌套其它组件的定义
c3
和c4
是Map
组件的两种不同定义形式。第一种只反对String
类型的key,第二种反对任意数据类型的key
援用组件
ByxContainer既反对对全局组件的援用,也反对对部分组件的援用。
键 | 类型 | 阐明 | 是否必须 |
---|---|---|---|
ref | 字符串 | 援用的组件key | 是 |
{ "components": { "a1": {"class": "byx.test.A"}, "a2": {"ref": "a1"} }}
以上配置等价于以下Java代码:
A a1 = new A();A a2 = a1;
注:对于部分组件,请参考这里。
条件组件
ByxContainer反对依据不同条件产生不同的组件定义。
键 | 类型 | 阐明 | 是否必须 |
---|---|---|---|
if | 对象或根本类型 | 作为条件的组件定义 | 是 |
then | 对象或根本类型 | 条件为真时产生的组件 | 是 |
else | 对象或根本类型 | 条件为假时产生的组件 | 是 |
{ "components": { "flag": true, "c": { "if": {"ref": "flag"}, "then": {"class": "com.byx.A"}, "else": {"class": "com.byx.B"} } }}
以上配置等价于以下Java代码:
boolean flag = true;Object c;if (flag) c = new A();else c = new B();
注:当if
设置成一个组件定义时,容器将把该组件创立的对象解释成boolean
值,如果该组件创立的对象不是boolean
类型,则当成false
解决。
高级个性
ByxContainer还反对以下高级个性:
- 部分组件
- 单例组件
- 自定义组件
- 自定义转换器
- 类型别名
部分组件
有时候,某些组件的创立过程中须要用到其它的组件,如上面这个例子:
A a = new A("hello", "123", new B());
咱们能够依照如下形式来配置:
{ "components": { "msg": "hello", "val": 123, "b": {"class": "byx.test.B"}, "a": { "class": "byx.test.A", "parameters": [{"ref": "msg"}, {"ref": "val"}, {"ref": "b"}] } }}
在A
的构造函数中传递的三个参数别离属于三个不同的组件,然而这三个组件仅仅用于构建A
对象,咱们不心愿这些长期组件占用容器中的一个key。当然,能够应用一种“就地初始化”的写法:
{ "components": { "a": { "class": "byx.test.A", "parameters": [ "hello", 123, {"class": "byx.test.B"} ] } }}
这种写法能够暗藏长期组件,然而当组件创立过程比较复杂的时候,这种写法会造成深层次的嵌套构造,导致可读性变差。而且,这种写法也不能很好地应答某个长期组件被屡次应用的状况,例如:
B b = new B();A a = new A(b, b);
以上问题能够应用ByxContainer提供的部分组件来解决:
{ "components": { "a": { "class": "byx.test.A", "parameters": [{"ref": "msg"}, {"ref": "val"}, {"ref": "b"}], "locals": { "msg": "hello", "val": 123, "b": {"class": "byx.test.B"} } } }}
在a
组件定义的外部有一个locals
块,外面蕴含了属于a
的部分组件定义,这些部分组件不会占用容器的全局key,它只能在组件a
的作用域内被援用。如果部分组件的key与全局组件的key雷同,则部分组件将会笼罩全局组件。
单例组件
在默认状况下,ByxContainer中的所有组件都是单例的,这意味着组件最多只会被创立一次。当你屡次获取同一个组件时,所失去的是同一个对象实例:
Object c1 = container.getComponent("c1");Object c2 = container.getComponent("c2");// c1与c2指向同一个对象
如果想要扭转这个默认行为,只须要在组件定义中退出如下配置:
"singleton": false
自定义组件
ByxContainer反对用户定义本人的组件,并在配置文件中应用。
用户能够通过实现byx.container.component.Component
接口来创立本人的组件:
public interface Component{ Object create();}
Component
接口中的create
办法封装了对象创立的细节:
public class MyComponent implements Component{ private final String msg; private final int val; public MyComponent() { MyComponent("hello", 123); } public MyComponent(String msg, int val) { this.msg = msg; this.val = val; } @Override public Object create() { return new A(msg, val); }}
在配置文件中应用自定义组件,须要指定自定义组件的全限定类名,如果想传递结构函数参数,则需指定参数列表。
键 | 类型 | 阐明 | 是否必须 |
---|---|---|---|
custom | 字符串 | 自定义组件的全限定类名 | 是 |
parameters | 数组 | 传递给自定义组件的结构函数参数 | 否 |
{ "components": { "a1": { "custom": "byx.test.MyComponent" }, "a2": { "custom": "byx.test.MyComponent", "parameters": ["hi", 456] } }}
以上配置等价于以下Java代码:
A a1 = new MyComponent().create();A a2 = new MyComponent("hi", 456).create();
自定义转换器
ByxContainer反对对组件创立进去的对象进行进一步转换。用户须要通过实现byx.container.component.Mapper
接口来创立自定义转换器:
public interface Mapper{ Object map(Object obj);}
Mapper
接口中的map
办法封装了对组件创立后果的解决:
public class MyMapper implements Mapper{ private final int val; public MyMapper() { MyMapper(1); } public MyMapper(int val) { this.val = val; } @Override public Object map(Object obj) { if (obj instanceof Integer) return ((int) obj) + val; return obj; }}
在配置文件中应用自定义转换器,须要在组件定义中指定mapper
属性:
- 当
mapper
属性为字符串时,该字符串示意自定义mapper
的全限定类名,同时用默认构造函数来创立该mapper
- 当
mapper
属性为对象时,对象中必须蕴含class
和parameters
属性,其中class
示意mapper
的全限定类名,parameters
示意创立mapper
时向构造函数传递的参数
{ "components": { "c1": { "class": "java.lang.Integer", "parameters": [123], "mapper": "byx.test.MyMapper" }, "c1": { "class": "java.lang.Integer", "parameters": [123], "mapper": { "class": "byx.test.MyMapper", "parameters": [100] } } }}
以上配置等价于以下Java代码:
int c1 = (int) new MyMapper().map(container.getComponent("c1"));int c2 = (int) new MyMapper(100).map(container.getComponent("c2"));
类型别名
为了防止键入大量简短的全限定类名,ByxContainer提供了类型别名性能,只需在容器定义最外层增加typeAlias
配置:
{ "typeAlias": { "A": "byx.test.A", "B": "byx.test.B" }, "components": { "a": { "class": "A", "parameters": ["hello", 123] }, "b": { "class": "B" } }}
以上配置等价于以下Java代码:
A a = new A("hello", 123);B b = new B();
注:components
中所有呈现类名的中央都能够应用typeAlias
中定义的别名,包含class
、factory
、custom
等等。