ByxContainer是一個用Java撰寫的輕量級IOC容器,具有以下特性:
- 使用JSON格式的組態檔
- 支持建構式注入、靜態工廠注入、實體工廠注入、屬性注入、setter注入、條件注入
- 組件的延遲加載和單例組件
- 根據id注冊、獲取容器中的組件
專案地址:https://github.com/byx2000/ByxContainer
組態檔
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等等,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/258604.html
標籤:Java
上一篇:item "tracker_server" in file:/***/WEB-INF/lib/***.jar!/fdfs_client.conf not found
下一篇:01 Java多執行緒編程的方法
