javascript
我居然手写了Spring框架
手寫完了
剛參加工作那會(huì)接觸java還是用的struct的時(shí)代,后面在SSH火爆時(shí)代的時(shí)候我轉(zhuǎn)戰(zhàn).net,多年之后公司轉(zhuǎn)java技術(shù)棧已經(jīng)是Spring的天下,源碼嚼了很多遍于是很想嘗試把這套東西用在.net平臺(tái)上。社區(qū)有個(gè)Spring.net項(xiàng)目已經(jīng)多年不維護(hù)了,而且還是xml配置模式非基于注解的,無法與現(xiàn)有的SpringBoot項(xiàng)目同日而語。在SpringBoot項(xiàng)目中的常用的注解和擴(kuò)展機(jī)制我都在這個(gè)項(xiàng)目中實(shí)現(xiàn)了,可以看下面介紹的已實(shí)現(xiàn)的功能一覽!
Annotation是注解的意思,在java項(xiàng)目里面 注解的概念和 csharp里面的 Attribute 的概念是一樣的。
本項(xiàng)目是基于Autofac(巨人的肩膀)的基礎(chǔ)之上構(gòu)建,選擇用Autofac是它擴(kuò)展性非常好,在實(shí)現(xiàn)Spring的細(xì)節(jié)上提供了便捷
本項(xiàng)目的所有實(shí)現(xiàn)都參考Spring的設(shè)計(jì)思想,但是并不是純粹的把java的代碼換成csharp,功能上效果是和Spring看齊的,但代碼實(shí)現(xiàn)上是自己實(shí)現(xiàn)的
本項(xiàng)目的目的
基于參考 Java的 Spring注解方式開發(fā)思想,
所有容器的注冊(cè) 和 裝配 都是依賴標(biāo)簽來完成。
這樣一來 一方面很容易分清楚 哪些是DI 哪些非DI, 哪些是攔截器,哪些需要攔截器,輕松實(shí)現(xiàn)切面編程,
代碼也好看,吸收java的spring框架的優(yōu)越的地方,配合.net語法的優(yōu)越性,編程效率能夠大大提升。
本篇文章主要介紹高階玩法,基礎(chǔ)玩法可以看項(xiàng)目wiki
開源地址:https://github.com/yuzd/Autofac.Annotation
支持的標(biāo)簽一覽
| AutoConfiguration | 打在class上面 | 自動(dòng)裝配class里面帶有Bean標(biāo)簽的方法 |
| Bean | 打在方法上面 | 配合AutoConfiguration標(biāo)簽使用 |
| Component | 打在class上面 | 自動(dòng)注冊(cè) |
| Autowired | 打在構(gòu)造方法的Parameter,類的Property,類的Field | 自動(dòng)裝配 |
| PropertySource | 打在class上面 | 配合Value標(biāo)簽使用,設(shè)置Value的數(shù)據(jù)源,支持json,xml,支持資源內(nèi)嵌 |
| Value | 打在構(gòu)造方法的Parameter,類的Property,類的Field | 靜態(tài)/動(dòng)態(tài)(例如nacos)數(shù)據(jù)裝配,支持強(qiáng)大的EL表達(dá)式 |
| Pointcut | 打在class上面 | 切面配置,一個(gè)切面攔截N多個(gè)對(duì)象,配合Before After AfterReturn AfterThrows Around 實(shí)現(xiàn)攔截器鏈 |
| Import | 打在繼承了ImportSelector的class上面 | 擴(kuò)展注冊(cè)Component |
| Order | 打在了class上面,和Compoment一起使用 | 值越小的越先加載 |
| Conditional | 打在class或者方法上面 | 條件加載,自定義實(shí)現(xiàn)的 |
| ConditionOnBean | 打在標(biāo)有Bean注解的方法上面 | 條件加載 |
| ConditionOnMissingBean | 打在標(biāo)有Bean注解的方法上面 | 條件加載 |
| ConditionOnClass | 打在class或者方法上面 | 條件加載 |
| ConditionOnMissingClass | 打在class或者方法上面 | 條件加載 |
| ConditionOnProperty | 打在class或者方法上面 | 條件加載 |
| ConditionOnProperties | 打在class或者方法上面 | 條件加載 |
| PostConstruct | 打在方法上面 | 當(dāng)類初始化完成后調(diào)用 |
| PreDestory | 打在方法上面 | 當(dāng)容器Dispose前調(diào)用 |
基本使用略過
基本使用可以參考詳細(xì)的wiki文檔:
Wiki
下面講講高階玩法
1. 攔截器原理簡(jiǎn)單介紹
用了Castle.Core組件 把你想要實(shí)現(xiàn)攔截器的目標(biāo)類生成一個(gè)代理類。
然后織入攔截器,有2種方式
類攔截器:class + 方法為virtual的方式
這種方式需要 從容器中是根據(jù)一個(gè)classType來獲取到目標(biāo)實(shí)例
接口型攔截器:interface + 方法重寫的方式
這種方式需要 從容器中是根據(jù)一個(gè)interfaceType來獲取到目標(biāo)實(shí)例
攔截器開關(guān)
在你想要實(shí)現(xiàn)攔截的目標(biāo)類上打開開關(guān) 【[Component(EnableAspect = true)]】,如上面的解釋,打開Aspect開關(guān)標(biāo)識(shí)這個(gè)class你想要走代理包裝,還可以根據(jù)InterceptorType屬性值設(shè)定你是哪種方式的攔截器
InterceptorType屬性解釋 Class 使用class的虛方法模式 【默認(rèn)方式】 Interface 使用接口模式 目的是打個(gè)標(biāo)簽就能夠攔截目標(biāo)方法
使得我們自定義的方法能夠
在指定的目標(biāo)方法執(zhí)行之前先執(zhí)行(比如參數(shù)校驗(yàn))
或者在指定的目標(biāo)方法執(zhí)行之后執(zhí)行(比如說檢驗(yàn)返回值,或其他收尾工作)
或者環(huán)繞目標(biāo)的方法,比如日志or事務(wù):TransactionScope或者記錄方法執(zhí)行的時(shí)間或者日志
攔截器標(biāo)簽攔截器類型使用說明 AspectArround(抽象標(biāo)簽類) 環(huán)繞攔截 重寫OnInvocation方法 AspectBefore(抽象標(biāo)簽類) 前置攔截器 重寫B(tài)efore方法 AspectAfter(抽象標(biāo)簽類) 后置攔截器(不管目標(biāo)方法成功失敗都會(huì)執(zhí)行) 重寫After方法 AspectAfterReturn(抽象標(biāo)簽類) 后置攔截器(只有目標(biāo)方法成功才會(huì)執(zhí)行) 重寫AfterReturn方法 AspectAfterThrows(抽象標(biāo)簽類) 錯(cuò)誤攔截器(只有目標(biāo)方法失敗才會(huì)執(zhí)行) 重寫AfterThrows方法 每個(gè)攔截器方法都有一個(gè)
攔截器的方法參數(shù) AspectContext 屬性說明
名稱說明 ComponentContext DI容器,可以從中取得你已注冊(cè)的實(shí)例 Arguments 目標(biāo)方法的參數(shù) TargetMethod 目標(biāo)方法的MethodInfo ReturnValue 目標(biāo)方法的返回 Method 目標(biāo)方法的代理方法MethodInfo 前置攔截器 (Before)
首先要自己寫一個(gè)類繼承 前置攔截器AspectBefore(抽象標(biāo)簽類)
實(shí)現(xiàn)該抽象類的Before方法
public class TestHelloBefore:AspectBefore{public override Task Before(AspectContext aspectContext){Console.WriteLine("TestHelloBefore");return Task.CompletedTask;}}[Component(EnableAspect = true)]//注意這里需要打開開關(guān) 否則無效public class TestHello{[TestHelloBefore]public virtual void Say(){Console.WriteLine("Say");}}
首先要自己寫一個(gè)類繼承后置攔截器AspectAfter(抽象標(biāo)簽類)
實(shí)現(xiàn)該抽象類的After方法
public class TestHelloAfter:AspectAfter{//這個(gè) returnValue 如果目標(biāo)方法正常返回的話 那就是目標(biāo)方法的返回值// 如果目標(biāo)方法拋異常的話 那就是異常本身public override Task After(AspectContext aspectContext,object returnValue){Console.WriteLine("TestHelloAfter");return Task.CompletedTask;}}[Component(EnableAspect = true)]public class TestHello{[TestHelloAfter]public virtual void Say(){Console.WriteLine("Say");}}
首先要自己寫一個(gè)類繼承攔截器AspectReturn(抽象標(biāo)簽類)
實(shí)現(xiàn)該抽象類的After方法
public class TestHelloAfterReturn:AspectAfterReturn{//result 是目標(biāo)方法的返回 (如果目標(biāo)方法是void 則為null)public override Task AfterReturn(AspectContext aspectContext, object result){Console.WriteLine("TestHelloAfterReturn");return Task.CompletedTask;}}[Component(EnableAspect = true)]public class TestHello{[TestHelloAfterReturn]public virtual void Say(){Console.WriteLine("Say");}}
首先要自己寫一個(gè)類繼承攔截器AspectReturn(抽象標(biāo)簽類)
實(shí)現(xiàn)該抽象類的After方法
public class TestHelloAfterThrows:AspectAfterThrows{public override Task AfterThrows(AspectContext aspectContext, Exception exception){Console.WriteLine(exception.Message);return Task.CompletedTask;}}[Component(EnableAspect = true)]public class TestHello{[TestHelloAfterThrows]public virtual void Say(){Console.WriteLine("Say");throw new ArgumentException("exception");}}
首先要自己寫一個(gè)類繼承攔截器AspectArround(抽象標(biāo)簽類)
實(shí)現(xiàn)該抽象類的OnInvocation方法
public class TestHelloAround:AspectArround{public override async Task OnInvocation(AspectContext aspectContext, AspectDelegate _next){Console.WriteLine("around start");await _next(aspectContext);Console.WriteLine("around end");}}[Component(EnableAspect = true)]public class TestHello{[TestHelloAround]public virtual void Say(){Console.WriteLine("Say");}}
先執(zhí)行TestHelloAround的OnInvocation方法
然后TestHelloAround的OnInvocation方法里面執(zhí)行的 await _next(aspectContext); 就會(huì)執(zhí)行被攔截方法TestHello的Say方法;
先執(zhí)行TestHelloAround,打印 “around start” 然后執(zhí)行到里面的_next(aspectContext)會(huì)觸發(fā)下面
執(zhí)行TestHelloBefore 打印 “TestHelloBefore”
執(zhí)行目標(biāo)方法 打印 “Say”
打印 “around end” TestHelloAround運(yùn)行結(jié)束
執(zhí)行TestHelloAfter 打印 “TestHelloAfter”
因?yàn)槭悄繕?biāo)方法成功執(zhí)行 TestHelloAfterReturn 打印 “TestHelloAfterReturn”
先執(zhí)行TestHelloAround,打印 “around start” 然后執(zhí)行到里面的_next(aspectContext)會(huì)觸發(fā)下面
執(zhí)行TestHelloBefore 打印 “TestHelloBefore”
執(zhí)行目標(biāo)方法 打印 “Say”
打印 “around end” TestHelloAround運(yùn)行結(jié)束
執(zhí)行TestHelloAfter 打印 “TestHelloAfter”
因?yàn)槭悄繕?biāo)方法異常 執(zhí)行 TestHelloAfterThrows 打印異常信息
先執(zhí)行TestHelloAround2 打印 “TestHelloAround2 start” 然后執(zhí)行到里面的_next(aspectContext)會(huì)觸發(fā)下面
執(zhí)行TestHelloBefore2 打印 “TestHelloBefore2” 然后進(jìn)入到
執(zhí)行TestHelloAround1 打印 “TestHelloAround1 start” 然后執(zhí)行到里面的 _next(aspectContext)會(huì)觸發(fā)下面
執(zhí)行TestHelloBefore1 打印 “TestHelloBefore1”
執(zhí)行目標(biāo)方法 SayGroup 打印 “SayGroup”
TestHelloAround1運(yùn)行結(jié)束 打印 “TestHelloAround1 end”
執(zhí)行 TestHelloAfter1 打印 “TestHelloAfter1”
執(zhí)行 TestHelloAfterReturn1 打印 “TestHelloAfterReturn1”
TestHelloAround2運(yùn)行結(jié)束 打印 “TestHelloAround2 end”
執(zhí)行 TestHelloAfter2 打印 “TestHelloAfter2”
執(zhí)行 TestHelloAfterReturn2 打印 “TestHelloAfterReturn2”
執(zhí)行的順序如下圖
因?yàn)橐粋€(gè)class上可以打多個(gè)Pointcut切面,一個(gè)Pointcut切面可以根據(jù)name去匹配對(duì)應(yīng)攔截方法
Around切入點(diǎn) 必須要指定?AspectContext類型?和 AspectDelegate類型的2個(gè)參數(shù),且返回類型要是Task 否則會(huì)報(bào)錯(cuò)
除了Around切入點(diǎn)以外其他的切入點(diǎn)的返回值只能是Task或者Void 否則會(huì)報(bào)錯(cuò)
除了Around切入點(diǎn)以外其他的切入點(diǎn)可以指定?AspectContext類型?參數(shù)注入進(jìn)來
After切入點(diǎn) 可以指定Returing參數(shù),可以把目標(biāo)方法的返回注入進(jìn)來,如果目標(biāo)方法拋異常則是異常本身
AfterReturn切入點(diǎn) 可以指定Returing參數(shù),可以把目標(biāo)方法的返回注入進(jìn)來
AfterThrows切入點(diǎn) 可以指定 Throwing參數(shù),可以把目標(biāo)方法拋出的異常注入進(jìn)來
只要你參數(shù)類型是你注冊(cè)到DI容器,運(yùn)行時(shí)會(huì)自動(dòng)從DI容器把類型注入進(jìn)來
可以使用Autowired,Value標(biāo)簽來修飾參數(shù)
Pointcut1Controller.TestSuccess 和 TestThrow 2個(gè)方法 會(huì)被匹配
Pointcut2Controller.TestThrow 和 TestThrow 2個(gè)方法 會(huì)被匹配
PostProcessBeforeInitialization
PostProcessAfterInitialization
前置攔截器方法的執(zhí)行順序?yàn)?#xff1a;先執(zhí)行 TestHelloBefor的Before方法再執(zhí)行你的Say方法
后置攔截器 (After) 不管目標(biāo)方法成功還是拋異常都會(huì)執(zhí)行
執(zhí)行順序?yàn)?#xff1a;先執(zhí)行你的SayAfter方法再執(zhí)行 TestHelloAfter的After方法
這里要特別注意的是 After 攔截器 是不管你的目標(biāo)方法(SayAfter是成功還是拋異常)
都被會(huì)執(zhí)行到的
成功返回?cái)r截器 (AfterReturn)只有目標(biāo)方法成功的時(shí)候才會(huì)執(zhí)行
執(zhí)行順序?yàn)?#xff1a;先執(zhí)行你的Say方法再執(zhí)行 TestHelloAfterReturn的AfterReturn方法
如果你的Say方法拋出異常那么就不會(huì)執(zhí)行TestHelloAfterReturn的AfterReturn方法
異常攔截器 (AfterThrows)
執(zhí)行順序?yàn)?#xff1a;先執(zhí)行你的Say方法再執(zhí)行 TestHelloAfterThrows的AfterThrows方法
如果你的Say方法不拋出異常那么就不會(huì)執(zhí)行 TestHelloAfterThrows的AfterThrows方法
環(huán)繞攔截器(Around)
注意:OnInvocation方法除了AspectContext參數(shù)以外 還有一個(gè) AspectDelegate _next 參數(shù),
需要在你的Around攔截器方法顯示調(diào)用 _next(aspectContext) 方法,否則目標(biāo)方法不會(huì)被調(diào)用
方法的執(zhí)行順序?yàn)?#xff1a;
如果Around Befor After AfterReturn AfterThrows 一起用
正常case
[Component(EnableAspect = true)]public class TestHello{[TestHelloAround,TestHelloBefore,TestHelloAfter,TestHelloAfterReturn,TestHelloAfterThrows]public virtual void Say(){Console.WriteLine("Say");}}代碼的執(zhí)行順序?yàn)?#xff1a;
由于是目標(biāo)方法成功返回 沒有異常,所以不會(huì)走進(jìn)TestHelloAfterThrows
異常case
[Component(EnableAspect = true)]public class TestHello{[TestHelloAround,TestHelloBefore,TestHelloAfter,TestHelloAfterReturn,TestHelloAfterThrows]public virtual void Say(){Console.WriteLine("Say");throw new ArgumentException("exception");}}代碼的執(zhí)行順序?yàn)?#xff1a;
如上述執(zhí)行順序和spring是一致的
多組的情況
public class TestHelloBefore1:AspectBefore{public override Task Before(AspectContext aspectContext){Console.WriteLine("TestHelloBefore1");return Task.CompletedTask;}}public class TestHelloAfter1:AspectAfter{//這個(gè) returnValue 如果目標(biāo)方法正常返回的話 那就是目標(biāo)方法的返回值// 如果目標(biāo)方法拋異常的話 那就是異常本身public override Task After(AspectContext aspectContext,object returnValue){Console.WriteLine("TestHelloAfter1");return Task.CompletedTask;}}public class TestHelloAfterReturn1:AspectAfterReturn{//result 是目標(biāo)方法的返回 (如果目標(biāo)方法是void 則為null)public override Task AfterReturn(AspectContext aspectContext, object result){Console.WriteLine("TestHelloAfterReturn1");return Task.CompletedTask;}}public class TestHelloAround1:AspectArround{public override async Task OnInvocation(AspectContext aspectContext, AspectDelegate _next){Console.WriteLine("TestHelloAround1 start");await _next(aspectContext);Console.WriteLine("TestHelloAround1 end");}}public class TestHelloAfterThrows1:AspectAfterThrows{public override Task AfterThrows(AspectContext aspectContext, Exception exception){Console.WriteLine("TestHelloAfterThrows1");return Task.CompletedTask;}}//public class TestHelloBefore2:AspectBefore{public override Task Before(AspectContext aspectContext){Console.WriteLine("TestHelloBefore2");return Task.CompletedTask;}}public class TestHelloAfter2:AspectAfter{//這個(gè) returnValue 如果目標(biāo)方法正常返回的話 那就是目標(biāo)方法的返回值// 如果目標(biāo)方法拋異常的話 那就是異常本身public override Task After(AspectContext aspectContext,object returnValue){Console.WriteLine("TestHelloAfter2");return Task.CompletedTask;}}public class TestHelloAfterReturn2:AspectAfterReturn{//result 是目標(biāo)方法的返回 (如果目標(biāo)方法是void 則為null)public override Task AfterReturn(AspectContext aspectContext, object result){Console.WriteLine("TestHelloAfterReturn2");return Task.CompletedTask;}}public class TestHelloAround2:AspectArround{public override async Task OnInvocation(AspectContext aspectContext, AspectDelegate _next){Console.WriteLine("TestHelloAround2 start");await _next(aspectContext);Console.WriteLine("TestHelloAround2 end");}}public class TestHelloAfterThrows2:AspectAfterThrows{public override Task AfterThrows(AspectContext aspectContext, Exception exception){Console.WriteLine("TestHelloAfterThrows2");return Task.CompletedTask;}}[Component(EnableAspect = true)]public class TestHello{[TestHelloAround1(GroupName = "Aspect1",OrderIndex = 10),TestHelloBefore1(GroupName = "Aspect1",OrderIndex = 10),TestHelloAfter1(GroupName = "Aspect1",OrderIndex = 10),TestHelloAfterReturn1(GroupName = "Aspect1",OrderIndex = 10),TestHelloAfterThrows1(GroupName = "Aspect1",OrderIndex = 10)][TestHelloAround2(GroupName = "Aspect2",OrderIndex = 1),TestHelloBefore2(GroupName = "Aspect2",OrderIndex = 1),TestHelloAfter2(GroupName = "Aspect2",OrderIndex = 1),TestHelloAfterReturn2(GroupName = "Aspect2",OrderIndex = 1),TestHelloAfterThrows2(GroupName = "Aspect2",OrderIndex = 1)]public virtual void SayGroup(){Console.WriteLine("SayGroup");}}如上面的代碼在目標(biāo)方法上打了2組 那么對(duì)應(yīng)的執(zhí)行順序是:
2. 面向切面編程
上面介紹了利用Aspect標(biāo)簽來完成攔截器功能
Aspect是一對(duì)一的方式,我想要某個(gè)class開啟攔截器功能我需要針對(duì)每個(gè)class去配置。
比如說 我有2個(gè) controller 每個(gè)controller都有2個(gè)action方法,
[Component]public class ProductController{public virtual string GetProduct(string productId){return "GetProduct:" + productId;}public virtual string UpdateProduct(string productId){return "UpdateProduct:" + productId;}}[Component]public class UserController{public virtual string GetUser(string userId){return "GetUser:" + userId;}public virtual string DeleteUser(string userId){return "DeleteUser:" + userId;}}如果我需要這2個(gè)controller的action方法都在執(zhí)行方法前打log 在方法執(zhí)行后打log
按照上一節(jié)Aspect的話 我需要每個(gè)controller都要配置。如果我有100個(gè)controller的話我就需要配置100次,這樣我覺得太麻煩了。所以我參考了Spring的Pointcut切面編程的方式實(shí)現(xiàn)了,下面看如何用Pointcut的方式方便的配置一種切面去適用于N個(gè)對(duì)象。
定義一個(gè)切面:創(chuàng)建一個(gè)class 上面打上Pointcut的標(biāo)簽 如下:
Pointcut標(biāo)簽類有如下屬性:
| Name | 名稱Pointcut切面的名稱(默認(rèn)為空,和攔截方法進(jìn)行匹配,參考下面說明) |
| RetType | 匹配目標(biāo)類的方法的返回類型(默認(rèn)是%) |
| NameSpace | 匹配目標(biāo)類的namespace(默認(rèn)是%) |
| ClassName | 匹配目標(biāo)類的類名稱(和下面的AttributeType參數(shù)二選一必填) |
| AttributeType | 匹配特定的標(biāo)簽(和上面的ClassName參數(shù)二選一必填) |
| MethodName | 匹配目標(biāo)類的方法名稱(默認(rèn)是%) |
切面如何匹配
// *Controller 代表匹配 只要是Controller結(jié)尾的類都能匹配// Get* 代表上面匹配成功的類下 所以是Get打頭的方法都能匹配[Pointcut(Class = "*Controller",Method = "Get*")]public class LoggerPointCut{} // *Controller 代表匹配 只要是Controller結(jié)尾的類都能匹配// Get* 代表上面匹配成功的類下 所以是Get打頭的方法都能匹配[Pointcut(ClassName = "*Controller",MethodName = "Get*")]public class LoggerPointCut{}定義好了一個(gè)Pointcut切面后 需要定義這個(gè)切面的攔截方法(也叫切入點(diǎn))
配合Pointcut切面標(biāo)簽,可以在打了這個(gè)標(biāo)簽的class下定義攔截方法,
在方法上得打上特定的標(biāo)簽,有如下幾種:
| Before標(biāo)簽 | 在匹配成功的類的方法執(zhí)行前執(zhí)行 |
| After標(biāo)簽 | 在匹配成功的類的方法執(zhí)行后執(zhí)行(不管目標(biāo)方法成功還是失敗) |
| AfterReturn標(biāo)簽 | 在匹配成功的類的方法執(zhí)行后執(zhí)行(只是目標(biāo)方法成功) |
| AfterThrows標(biāo)簽 | 在匹配成功的類的方法執(zhí)行后執(zhí)行(只是目標(biāo)方法拋異常時(shí)) |
| Around標(biāo)簽 | 環(huán)繞目標(biāo)方法,承接了匹配成功的類的方法的執(zhí)行權(quán) |
以上3種標(biāo)簽有一個(gè)可選的參數(shù):Name (默認(rèn)為空,可以和Pointcut的Name進(jìn)行mapping)
切入點(diǎn)標(biāo)簽所在方法的參數(shù)說明:
按照上面的配置
執(zhí)行順序
單個(gè)切面順序如下圖
多個(gè)切面執(zhí)行的順序如下圖
關(guān)于順序是和上面用Aspect是一致的,只不過是1:N,1個(gè)切面來切N個(gè)目標(biāo)
切面功能與Spring相比缺少了一個(gè)靈活的切點(diǎn)表達(dá)式,所以功能會(huì)弱很多,這塊目前我還沒有很好的設(shè)計(jì)思路,歡迎來教育!
3. BeanPostProcessor的設(shè)計(jì)
參考Spring框架,
在類的初始化過程中進(jìn)行自定義邏輯而設(shè)計(jì)的BeanPostProcessor,有2個(gè)方法:
1. PostProcessBeforeInitialization
該方法在bean實(shí)例化完畢(且已經(jīng)注入完畢),屬性設(shè)置或自定義init方法執(zhí)行之前執(zhí)行!
2. PostProcessAfterInitialization
該方法在bean實(shí)例化完畢(且已經(jīng)注入完畢),在屬性設(shè)置或自定義init方法執(zhí)行之后
一個(gè)使用場(chǎng)景例子:自定義一個(gè)注解來封裝自定義邏輯
先定義一個(gè)自定義注解
/// <summary> /// 測(cè)試自己實(shí)現(xiàn)一個(gè)自定義註解 /// </summary> [AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)] public sealed class Soa : Attribute {/// <summary>/// 構(gòu)造函數(shù)/// </summary>public Soa(Type type){Type = type;}/// <summary>/// 注冊(cè)的類型/// </summary>internal Type Type { get; set; } }這個(gè)注解的名字叫Soa,然后有一個(gè)構(gòu)造方法,傳參為一個(gè)Class Type
下面需要實(shí)現(xiàn)一個(gè)BeanPostProcessor
[Component] public class SoaProcessor : BeanPostProcessor {//在實(shí)例化后且屬性設(shè)值之前執(zhí)行public object PostProcessBeforeInitialization(object bean){Type type = bean.GetType();找到bean下所有的字段var fieldInfos = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);foreach (var field in fieldInfos){//看字段上面有沒有打Soa自定義注解var soaAnnotation = field.GetCustomAttribute(typeof(Soa)) as Soa;if (soaAnnotation == null){continue;}//有的話根據(jù)注解的參數(shù)Type來實(shí)例化對(duì)象并設(shè)值var instance = Activator.CreateInstance(soaAnnotation.Type) as ISoa;if (instance == null){continue;}field.SetValue(bean, instance);}return bean;}//不管返回public object PostProcessAfterInitialization(object bean){return bean;} }好了,實(shí)現(xiàn)一個(gè)BeanPostProcessor就是寫一個(gè)類繼承并實(shí)現(xiàn)它的接口即可。
然后打上[Compoment]注冊(cè)到容器中即可。
下面測(cè)試效果
[Component] public class Test11Models1 {[Soa(typeof(SoaTest1))] private ISoa Soa1;[Soa(typeof(SoaTest2))] private ISoa Soa2;public string getSoa1(){return Soa1.say();}public string getSoa2(){return Soa2.say();} }public interface ISoa {string say(); }public class SoaTest1 : ISoa {public string say(){return nameof(SoaTest1);} }public class SoaTest2 : ISoa {public string say(){return nameof(SoaTest2);} }單元測(cè)試一下
[Fact] public void Test1() {var builder = new ContainerBuilder();builder.RegisterSpring(r => r.RegisterAssembly(typeof(TestBeanPostProcessor).Assembly));var container = builder.Build();var isRegisterd = container.TryResolve(out Test11Models1 model1);Assert.True(isRegisterd);Assert.Equal("SoaTest1",model1.getSoa1());Assert.Equal("SoaTest2",model1.getSoa2()); }Test11Models1這個(gè)類打了[Compoment]注冊(cè)到容器,當(dāng)從容器獲取它的時(shí)候會(huì)走到上面的SoaProcessor。然后識(shí)別到里面有打了自定義注解[Soa],并根據(jù)注冊(cè)的參數(shù)實(shí)例化。
Spring是一個(gè)非常龐大的框架,里面包含了非常多的細(xì)節(jié),比如處理依賴循環(huán),單例如何Autowired多例,FactoryBean,代理類的生成以及兼容async await,新出的valueTask的方法代理等等,這個(gè)項(xiàng)目是我2018年開始寫的,多次重構(gòu),每次重構(gòu)也是反映對(duì)spring源碼的理解程度不一樣;這個(gè)過程非常有趣(一次次推翻我自以為看了源碼就‘懂了’spring),目前最新版4.0.4 基本上包含了常用的spring功能,還會(huì)不斷更新(看我是否越來越‘懂’spring),感興趣可以看看單元測(cè)試
我是正東,學(xué)的越多不知道也越多。如果決定去深究一個(gè)東西, 一定要完全搞懂, 并認(rèn)真總結(jié)一篇博客讓以后能在短時(shí)間拾起來 ( 因?yàn)椴桓愣愫茈y寫一篇半年后還能理解的博客 )
歡迎白嫖點(diǎn)贊!
總結(jié)
以上是生活随笔為你收集整理的我居然手写了Spring框架的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .net core ——微服务内通信Th
- 下一篇: NET流行高性能JSON框架-Json.