菜渣開(kāi)源一個(gè)基于?EMIT?的?AOP?庫(kù)(.NET?Core)的方法
Nuget 庫(kù)地址:https://www.nuget.org/packages/CZGL.AOP/
Github 庫(kù)地址:https://github.com/whuanle/CZGL.AOP
CZGL.AOP 是 基于 EMIT 編寫(xiě)的 一個(gè)簡(jiǎn)單輕量的AOP框架,支持非侵入式代理,支持.NET Core/ASP.NET Core,以及支持多種依賴注入框架。
1,快速入門
CZGL.AOP 使用比較簡(jiǎn)單,你只需要使用 [Interceptor]
特性標(biāo)記需要代理的類型,然后使用繼承 ActionAttribute
的特性標(biāo)記要被代理的方法或?qū)傩浴?/p>
1.1 繼承 ActionAttribute 特性
ActionAttribute
是用于代理方法或?qū)傩缘奶匦詷?biāo)記,不能直接使用,需要繼承后重寫(xiě)方法。
示例如下:
public class LogAttribute : ActionAttribute { public override void Before(AspectContext context) { Console.WriteLine("執(zhí)行前"); } public override object After(AspectContext context) { Console.WriteLine("執(zhí)行后"); if (context.IsMethod) return context.MethodResult; else if (context.IsProperty) return context.PropertyValue; return null; } }
Before
會(huì)在被代理的方法執(zhí)行前或被代理的屬性調(diào)用時(shí)生效,你可以通過(guò) AspectContext
上下文,獲取、修改傳遞的參數(shù)。
After 在方法執(zhí)行后或?qū)傩哉{(diào)用時(shí)生效,你可以通過(guò)上下文獲取、修改返回值。
1.2 標(biāo)記代理類型
在被代理的類型中,使用 [Interceptor]
特性來(lái)標(biāo)記,在需要代理的方法中,使用 繼承了 ActionAttribute
的特性來(lái)標(biāo)記。
此方法是侵入式的,需要在編譯前完成。
[Interceptor] public class Test : ITest { [Log] public virtual string A { get; set; } [Log] public virtual void MyMethod() { Console.WriteLine("運(yùn)行中"); } }
注意的是,一個(gè)方法或?qū)傩灾荒茉O(shè)置一個(gè)攔截器。
2,如何創(chuàng)建代理類型
CZGL.AOP 有多種生成代理類型的方式,下面介紹簡(jiǎn)單的方式。
請(qǐng)預(yù)先創(chuàng)建如下代碼:
public class LogAttribute : ActionAttribute { public override void Before(AspectContext context) { Console.WriteLine("執(zhí)行前"); } public override object After(AspectContext context) { Console.WriteLine("執(zhí)行后"); if (context.IsMethod) return context.MethodResult; else if (context.IsProperty) return context.PropertyValue; return null; } } public interface ITest { void MyMethod(); } [Interceptor] public class Test : ITest { [Log] public virtual string A { get; set; } public Test() { Console.WriteLine("構(gòu)造函數(shù)沒(méi)問(wèn)題"); } [Log] public virtual void MyMethod() { Console.WriteLine("運(yùn)行中"); } }
2.1 通過(guò)API直接創(chuàng)建
通過(guò) CZGL.AOP 中的 AopInterceptor
類,你可以生成代理類型。
示例如下:
ITest test1 = AopInterceptor.CreateProxyOfInterface<ITest, Test>(); Test test2 = AopInterceptor.CreateProxyOfClass<Test>(); test1.MyMethod(); test2.MyMethod();
CreateProxyOfInterface
通過(guò)接口創(chuàng)建代理類型;CreateProxyOfClass
通過(guò)類創(chuàng)建代理類型;
默認(rèn)調(diào)用的是無(wú)參構(gòu)造函數(shù)。
3,創(chuàng)建代理類型
通過(guò)API
你可以參考源碼解決方案
中的 ExampleConsole 項(xiàng)目。
如果要直接使用 AopInterceptor.CreateProxyOfInterface
和 AopInterceptor.CreateProxyOfClass
方法,通過(guò)接口或類來(lái)創(chuàng)建代理類型。
ITest test1 = AopInterceptor.CreateProxyOfInterface<ITest, Test>(); Test test2 = AopInterceptor.CreateProxyOfClass<Test>();
如果要指定實(shí)例化的構(gòu)造函數(shù),可以這樣:
// 指定構(gòu)造函數(shù) test2 = AopInterceptor.CreateProxyOfClass<Test>("aaa", "bbb"); test2.MyMethod();
通過(guò) Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.DependencyInjection
是 .NET Core/ASP.NET Core 默認(rèn)的依賴注入容器。
如果需要支持 ASP.NET Core 中使用 AOP,你可以在 Nuget 包中安裝 CZGL.AOP.MEDI
。
如果你在控制臺(tái)下使用 Microsoft.Extensions.DependencyInjection
,你可以使用名為 BuildAopProxy
的 IServiceCollection
拓展方法來(lái)為容器中的類型,生成代理類型。
示例如下:
IServiceCollection _services = new ServiceCollection(); _services.AddTransient<ITest, Test>(); var serviceProvider = _services.BuildAopProxy().BuildServiceProvider(); serviceProvider.GetService<ITest>(); return serviceProvider;
你可以參考源碼解決方案中的 ExampleMEDI
項(xiàng)目。
如果你要在 ASP.NET Core 中使用,你可以在 Startup
中,ConfigureServices
方法的最后一行代碼使用 services.BuildAopProxy();
。
public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.BuildAopProxy(); }
還可以在 Program
的 IHostBuilder
中使用 .UseServiceProviderFactory(new AOPServiceProxviderFactory())
來(lái)配置使用 CZGL.AOP。
示例:
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .UseServiceProviderFactory(new AOPServiceProxviderFactory()) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
可以參考解決方案中的 ExampleConsole
和 ExampleWebMEDI
兩個(gè)項(xiàng)目。
你不必?fù)?dān)心引入 CZGL.AOP 后,使用依賴注入會(huì)使程序變慢或者破壞容器中的原有屬性。CZGL.AOP 只會(huì)在創(chuàng)建容器時(shí)處理需要被代理的類型,不會(huì)影響容器中的服務(wù),也不會(huì)干擾到依賴注入的執(zhí)行。
通過(guò) Autofac
如果需要在 Autofac 中使用 AOP,則需要引用 CZGL.AOP.Autofac
包。
如果你在控制臺(tái)程序中使用 Autofac,則可以在 Build()
后面使用 BuildAopProxy()
。
ContainerBuilder builder = new ContainerBuilder(); builder.RegisterType<Test>().As<ITest>(); var container = builder.Build().BuildAopProxy(); using (ILifetimeScope scope = container.BeginLifetimeScope()) { // 獲取實(shí)例 ITest myService = scope.Resolve<ITest>(); myService.MyMethod(); } Console.ReadKey(); }
要注意的是,在已經(jīng)完成的組件注冊(cè)創(chuàng)建一個(gè)新的容器后,才能調(diào)用 BuildAopProxy()
方法,
這樣針對(duì)一個(gè)新的容器你可以考慮是否需要對(duì)容器中的組件進(jìn)行代理。
如果在 ASP.NET Core 中使用 Autofac,你需要在 Program 類的 IHostBuilder 中使用:
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
如果需要代理已經(jīng)注冊(cè)的組件,則將其替換為:
.UseServiceProviderFactory(new CZGL.AOP.Autofac.AOPServiceProxviderFactory())
請(qǐng)參考 源碼解決方案中的 ExampleAutofac
和 ExampleWebAutofac
兩個(gè)項(xiàng)目。
4,深入使用
代理類型
要被代理的類型,需要使用 [Interceptor]
來(lái)標(biāo)記,例如:
[Interceptor] public class Test : ITest { }
支持泛型類型。
被代理的類型必須是可被繼承的。
類型的構(gòu)造函數(shù)沒(méi)有限制,你可以隨意編寫(xiě)。
在使用 API 創(chuàng)建代理類型并且實(shí)例化時(shí),你可以指定使用哪個(gè)構(gòu)造函數(shù)。
例如:
string a="",b="",c=""; ITest test1 = AopInterceptor.CreateProxyOfInterface<ITest, Test>(a,b,c);
API 會(huì)根據(jù)參數(shù)的多少以及參數(shù)的類型自動(dòng)尋找合適的構(gòu)造函數(shù)。
方法、屬性代理
為了代理方法或?qū)傩?,你需要繼承 ActionAttribute
特性,然后為方法或?qū)傩詷?biāo)記此特性,并且將方法或?qū)傩栽O(shè)置為 virtual
一個(gè)類型中的不同方法,可以使用不同的攔截器。
[Log1] public virtual void MyMethod1(){} [Log2] public virtual void MyMethod2(){}
對(duì)于屬性,可以在屬性上直接使用特性,或者只在 get 或 set 構(gòu)造器使用。
[Log] public virtual string A { get; set; } // 或 public virtual string A { [Log] get; set; } // 或 public virtual string A { get; [Log] set; }
如果在屬性上使用特性,相當(dāng)于 [Log] get; [Log] set;
。
上下文
一個(gè)簡(jiǎn)單的方法或?qū)傩詳r截標(biāo)記是這樣的:
public class LogAttribute : ActionAttribute { public override void Before(AspectContext context) { Console.WriteLine("執(zhí)行前"); } public override object After(AspectContext context) { Console.WriteLine("執(zhí)行后"); if (context.IsMethod) return context.MethodResult; else if (context.IsProperty) return context.PropertyValue; return null; } }
AspectContext 的屬性說(shuō)明如下:
字段 | 說(shuō)明 |
---|---|
Type | 當(dāng)前被代理類型生成的代理類型 |
ConstructorParamters | 類型被實(shí)例化時(shí)使用的構(gòu)造函數(shù)的參數(shù),如果構(gòu)造函數(shù)沒(méi)有參數(shù),則 MethodValues.Length = 0,而不是 MethodValues 為 null。 |
IsProperty | 當(dāng)前攔截的是屬性 |
PropertyInfo | 當(dāng)前被執(zhí)行的屬性的信息,可為 null。 |
PropertyValue | 但調(diào)用的是屬性時(shí),返回 get 的結(jié)果或 set 的 value 值。 |
IsMethod | 當(dāng)前攔截的是方法 |
MethodInfo | 當(dāng)前方法的信息 |
MethodValues | 方法被調(diào)用時(shí)傳遞的參數(shù),如果此方法沒(méi)有參數(shù),則 MethodValues.Length = 0,而不是 MethodValues 為 null |
MethodResult | 方法執(zhí)行返回的結(jié)果(如果有) |
攔截方法或?qū)傩缘膮?shù)
通過(guò)上下文,你可以修改方法或?qū)傩缘膮?shù)以及攔截返回結(jié)果:
public class LogAttribute : ActionAttribute { public override void Before(AspectContext context) { // 攔截并修改方法的參數(shù) for (int i = 0; i < context.MethodValues.Length; i++) { context.MethodValues[i] = (int)context.MethodValues[i] + 1; } Console.WriteLine("執(zhí)行前"); } public override object After(AspectContext context) { Console.WriteLine("執(zhí)行后"); // 攔截方法的執(zhí)行結(jié)果 context.MethodResult = (int)context.MethodResult + 664; if (context.IsMethod) return context.MethodResult; else if (context.IsProperty) return context.PropertyValue; return null; } } [Interceptor] public class Test { [Log] public virtual int Sum(int a, int b) { Console.WriteLine("運(yùn)行中"); return a + b; } }
Test test = AopInterceptor.CreateProxyOfClass<Test>(); Console.WriteLine(test.Sum(1, 1));
方法的參數(shù)支持 in
、ref
、out
;支持泛型方法泛型屬性;支持異步方法;
非侵入式代理
此種方式不需要改動(dòng)被代理的類型,你也可以代理程序集中的類型。
public class LogAttribute : ActionAttribute { public override void Before(AspectContext context) { Console.WriteLine("執(zhí)行前"); } public override object After(AspectContext context) { Console.WriteLine("執(zhí)行后"); if (context.IsMethod) return context.MethodResult; else if (context.IsProperty) return context.PropertyValue; return null; } }
public class TestNo { public virtual string A { get; set; } public virtual void MyMethod() { Console.WriteLine("運(yùn)行中"); } }
TestNo test3 = AopInterceptor.CreateProxyOfType<TestNo>(new ProxyTypeBuilder() .AddProxyMethod(typeof(LogAttribute), typeof(TestNo).GetMethod(nameof(TestNo.MyMethod))) .AddProxyMethod(typeof(LogAttribute), typeof(TestNo).GetProperty(nameof(TestNo.A)).GetSetMethod()));
通過(guò) ProxyTypeBuilder 來(lái)構(gòu)建代理類型。
代理方法或?qū)傩远际鞘褂?nbsp;AddProxyMethod
,第一個(gè)參數(shù)是要使用的攔截器,第二個(gè)參數(shù)是要攔截的方法。
如果要攔截屬性,請(qǐng)分開(kāi)設(shè)置屬性的 get
、set
構(gòu)造。
如果多個(gè)方法或?qū)傩允褂猛粋€(gè)攔截器,則可以這樣:
TestNo test3 = AopInterceptor.CreateProxyOfType<TestNo>( new ProxyTypeBuilder(new Type[] { typeof(LogAttribute) }) .AddProxyMethod("LogAttribute", typeof(TestNo).GetMethod(nameof(TestNo.MyMethod))) .AddProxyMethod("LogAttribute", typeof(TestNo).GetProperty(nameof(TestNo.A)).GetSetMethod()));
TestNo test3 = AopInterceptor.CreateProxyOfType<TestNo>( new ProxyTypeBuilder(new Type[] { typeof(LogAttribute) }) .AddProxyMethod("LogAttribute", typeof(TestNo).GetMethod(nameof(TestNo.MyMethod))) .AddProxyMethod(typeof(LogAttribute2), typeof(TestNo).GetProperty(nameof(TestNo.A)).GetSetMethod()));
在構(gòu)造函數(shù)中傳遞過(guò)去所需要的攔截器,然后在攔截時(shí)使用。
到此這篇關(guān)于菜渣開(kāi)源一個(gè)基于 EMIT 的 AOP 庫(kù)(.NET Core)的文章就介紹到這了,更多相關(guān)EMIT 的 AOP 庫(kù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
ASP.NET堆和棧三之引用類型對(duì)象拷貝和內(nèi)存分配
這篇文章介紹了ASP.NET堆和棧中引用類型對(duì)象的拷貝和內(nèi)存分配,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-08-08asp.net Datalist控件實(shí)現(xiàn)分頁(yè)功能
asp.net Datalist控件實(shí)現(xiàn)分頁(yè)功能代碼。大家可以參考下。2009-07-07大型門戶網(wǎng)站實(shí)現(xiàn)的十四大技術(shù)小結(jié)
參考下大型門戶網(wǎng)站的技術(shù),大家可以盡量的備份好服務(wù)器。2010-10-10詳解在ASP.NET Core下使用SignalR技術(shù)
本篇文章主要介紹了在ASP.NET Core下使用SignalR技術(shù) ,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-02-02asp.net部署到IIS常見(jiàn)問(wèn)題的解決方法
這篇文章主要為大家詳細(xì)介紹了asp.net部署到IIS常見(jiàn)問(wèn)題的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12基于ASP.NET+easyUI框架實(shí)現(xiàn)圖片上傳功能(表單)
這篇文章主要介紹了基于ASP.NET+easyUI框架實(shí)現(xiàn)圖片上傳功能的相關(guān)資料,需要的朋友可以參考下2016-06-06ASP.NET MVC自定義授權(quán)過(guò)濾器
這篇文章介紹了ASP.NET MVC自定義授權(quán)過(guò)濾器的用法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03