大家好,今天來介紹我開源的一個autofac.Annotation項目 源碼:https://github.com/yuzd/Autofac.Annotation
- 本項目是autofa的一個擴展組件,autofac是一個老牌的DI容器框架 ,支持netframework和netcore
- Annotdation是注解的意思,在java項目里面 注解的概念和 csharp里面的 Attribute 的概念是一樣的。
本項目的目的 降低玩DI容器的門檻,快速實現依賴注入 自動裝配 以及攔截器,AOP切面編程
基於參考 Java的 Spring注解方式開發思想,
所有autofac容器的注冊 和 裝配 都是依賴標簽來完成。
這樣一來 一方面很容易分清楚 哪些是DI 哪些非DI, 哪些是攔截器,哪些需要攔截器,輕松實現切面編程, 代碼也好看,吸收java的spring框架的優越的地方,配合.net語法的優越性,編程效率能夠大大提升。
支持的標簽一覽
標簽名稱 | 使用位置 | 使用說明 |
---|---|---|
AutoConfiguration | 打在class上面 | 自動裝配class里面帶有Bean標簽的方法 |
Bean | 打在方法上面 | 配合AutoConfiguration標簽使用 |
Component | 打在class上面 | 自動注冊 |
Autowired | 打在構造方法的Parameter,類的Property,類的Field | 自動裝配 |
PropertySource | 打在class上面 | 配合Value標簽使用,設置Value的數據源,支持json,xml,支持資源內嵌 |
Value | 打在構造方法的Parameter,類的Property,類的Field | 靜態數據裝配,支持強大的EL表達式 |
Aspect | 打在class上面 | 開啟攔截器,默認注冊為類攔截器,也可以指定為接口型攔截器(和pointcut的區別是它是打在哪個class哪個才會生效) |
Pointcut | 打在class上面 | 切面配置,一個切面攔截N多個對象 |
針對每個標簽的使用文檔 請移步wiki文檔傳送門
https://github.com/yuzd/Autofac.Annotation/wiki
下面介紹一下最最常用的功能也就是注冊和自動裝配
Componet標簽把類型注冊到DI容器
原理解釋
框架會掃描打了Componet標簽的class。如果Componet標簽里面指定了要注冊的類型,則會只注冊為這個類型到DI容器。如果沒有指定則會把當前class(參考下面的4),以及父類,以及接口都會注冊到DI容器(參考下面的1,2和3)。
如何修改這個默認配置呢,比如關閉自動注冊父類和接口。 可以參考 https://github.com/yuzd/Autofac.Annotation/issues/11
Componet有哪些屬性
屬性名稱 | 類型 | 含義 |
---|---|---|
Service | Type | 注冊指定單個的類型 |
Key | String | 注冊指定單個的key(為了同個類型注冊多次避免歧義) |
Services | Type[] | 注冊指定多個的類型 |
Keys | String[] | 注冊指定多個的key(如果指定多個類型又想避免歧義可以搭配上面一起使用) |
AutofacScope | Enum | InstancePerDependency(每次都是一個新實例,默認) SingleInstance(單例) InstancePerLifetimeScope(每個scope獲取新的實例) InstancePerRequest(根據每個請求一個實例) 可以在插件初始化的時候指定一個默認的 參考下方的截圖說明 |
AutoActivate | bool | 當指定Scope為單例的時候 默認false 當DI容器初始化完成后會自動完成實例化 |
InitMethod | string | 當實例化后自動執行的方法名稱 |
DestroyMethod | string | 當實例會DI容器回收會自動執行的方法名稱 |
Ownership | Enum | LifetimeScope(DI容器管理自動回收策略,默認)External(自己手動管理實例回收) |
Interceptor | Type | 指定攔截器類型 |
InterceptorType | Enum | Interface(使用接口模式) Class(使用class的虛方法模式) |
InterceptorKey | string | 如果同一個類型的攔截器有多個 可以指定Key |
InjectPropertyType | Enum | 屬性自動裝配的類型 ,Autowired(代表打了Autowired標簽的才會裝配),All(代表全部自動裝配) |
Componet的常用構造方法
- 默認的構造方法
//默認的構造方法會把當前class,以及父類,以及接口都會注冊到DI容器
//這里只會注冊A8到DI容器
[Component]
public class A8
{
}
//通過A8類型可以裝配成功
[Autowired]
public A8 A8 { get; set; }
·························································
public class B
{
}
//這里會把A8 和 B 都注冊到DI容器
[Component]
public class A8:B
{
}
//通過A8類型可以裝配成功
[Autowired]
public A8 A8 { get; set; }
//通過B類型也可以裝配成功 拿到的是A8類型
[Autowired]
public B B { get; set; }
··························································
public interface IA
{
}
public interface IB:IA
{
}
public class B:IB
{
}
//這里會把A8 B IB IA 全都注冊到DI容器
//如果你指向注冊A8 可以參考https://github.com/yuzd/Autofac.Annotation/issues/11 去配置
[Component]
public class A8:B
{
}
//通過A8類型可以裝配成功
[Autowired]
public A8 A8 { get; set; }
//通過B類型也可以裝配成功 拿到的是A8類型
[Autowired]
public B B { get; set; }
//通過IB類型也可以裝配成功 拿到的是A8類型
[Autowired]
public IB IB { get; set; }
//通過IA類型也可以裝配成功 拿到的是A8類型
[Autowired]
public IA IA { get; set; }
- 注冊為指定類型
public class B
{
}
//這個構造方法就是將A6注冊為B類型
[Component(typeof(B))]
public class A6:B
{
}
//可以通過下面的方式自動裝配 因為上面注冊的是B類型 通過B類型可以裝配成功 拿到的是A6類型
[Autowired]
public B B { get; set; }
//通過下面的方式自動裝配會失敗 會失敗 會失敗
[Autowired]
public A6 A6 { get; set; }
- 同一個注冊類型有多個 采用Key方式解決歧義
public interface ITestAutowiredModal
{
}
// ITestAutowiredModal這個類型被注冊多個了 避免歧義用"abc"來解決
[Component("abc")]
public class TestAutowiredModal1:ITestAutowiredModal
{
}
// ITestAutowiredModal這個類型被注冊多個了 避免歧義用"def"來解決
[Component("def")]
public class TestAutowiredModal2:ITestAutowiredModal
{
}
//可以用下面的方式來自動裝配 拿到的是TestAutowiredModal1類型對象
[Autowired("abc")]
public ITestAutowiredModal TestAutowiredModal1 { get; set; }
//如果不指定的話會先嘗試根據byType模式匹配,因為是指定了Key 所以根據byType拿不到,拿不到就會再次根據屬性名稱 “abc” 去匹配,就和上面的方式一樣了
[Autowired]
public ITestAutowiredModal abc { get; set; }
//可以用下面的方式來自動裝配 拿到的是TestAutowiredModal2類型對象
[Autowired("def")]
public ITestAutowiredModal TestAutowiredModal2 { get; set; }
1. 把一個類型注冊到DI容器
- 上面的例子就是把Student類型注冊到容器
2. 把當前類型和父類注冊到DI容器
- 上面的例子就是把Student2類型注冊到容器
- 並且把Person類型也注冊到容器根據Person類型拿到的是Student2的實體
3. 把當前類型和接口注冊到DI容器
- 上面的例子就是把Student3類型注冊到容器
- 並且把ISay也注冊到容器根據ISay類型拿到的是Student3的實體
4. 當繼承了父類或者接口 想要指定注冊類型怎么辦?
- 上面的例子就是只能通過ISay拿到Student3的實體
- 不能通過Student3類型拿到!!
5. 怎么指定實例是單例,每次都是新的實例,還是每個Scope一個實例呢?
- 如上圖所示 可以指定AutofacScope屬性
- 如果不指定就是每次獲取的一個新的實例
6. 當同一個類型多次注冊,如何區分得到我想要的?
- 如上圖 ISay類型有2個實現類 Student3 和 Student4 分別指定了 Key的值
- 通過ISay + “Student3” 可以獲取到 Student3的實體
- 通過ISay + “Student4” 可以獲取到 Student4的實體
7.支持注冊類型可以自動實例化對象
- 如上所示,設定AutoActivate = true代表是啟動自動實例化
- AutofacScope = AutofacScope.SingleInstance 代表單例模式
- Student5類型會對象會自動實例化 並且以單例的方式存儲在DI容器內
8.支持設置實例化時運行指定方法
- 如上所示 設置 InitMethod 和 DestroyMethod
- 當實例從DI容器初始化時就會調用 InitMethod
- 當DI容器Dispose的時候會觸發調用DestroyMethod
另外
- InitMethod支持注入
- DestroyMethod只能是無參數方法
Autowired自動裝配
Autowired有哪些屬性
屬性名稱 | 類型 | 含義 |
---|---|---|
Name | String | 搭配Component指定Key的時候使用,消除一個類型被多個注冊帶來的歧義 |
Required | bool | 默認裝載失敗會報錯 設置為false裝載失敗不會報錯 |
CircularDependencies | bool | 是否支持循環注入 默認是false 不支持,插件初始化的時候可以設置一個默認值 |
打在構造方法上 屬性
在單例的對象里面Autowired多實例
使用ObjectFactory來實現,和Lazy的區別是 ObjectFactory修飾的每次獲取都是從容器里面獲取一遍。而Lazy只有首次才會去容器獲取。
延遲自動裝配Lazy
[Component]
public class TestLazyModel1
{
[Autowired]
public Lazy<TestAutowiredModal4> TestAutowiredModal4 { get; set; }
[Autowired]
public TestAutowiredModal3 TestAutowiredModal3 { get; set; }
}