菜渣开源一个基于 EMIT 的 AOP 库(.NET Core)
Nuget 库地址:https://www.nuget.org/packages/CZGL.AOP/
Github 库地址:https://github.com/whuanle/CZGL.AOP
CZGL.AOP 是 基于 EMIT 编写的 一个简单轻量的AOP框架,支持非侵入式代理,支持.NET Core/ASP.NET Core,以及支持多种依赖注入框架。
1,快速入门
CZGL.AOP 使用比较简单,你只需要使用 [Interceptor] 特性标记需要代理的类型,然后使用继承 ActionAttribute  的特性标记要被代理的方法或属性。
1.1 继承 ActionAttribute 特性
ActionAttribute 是用于代理方法或属性的特性标记,不能直接使用,需要继承后重写方法。
示例如下:
    public class LogAttribute : ActionAttribute
    {
        public override void Before(AspectContext context)
        {
            Console.WriteLine("执行前");
        }
        public override object After(AspectContext context)
        {
            Console.WriteLine("执行后");
            if (context.IsMethod)
                return context.MethodResult;
            else if (context.IsProperty)
                return context.PropertyValue;
            return null;
        }
    }
Before 会在被代理的方法执行前或被代理的属性调用时生效,你可以通过 AspectContext 上下文,获取、修改传递的参数。
After 在方法执行后或属性调用时生效,你可以通过上下文获取、修改返回值。
1.2 标记代理类型
在被代理的类型中,使用 [Interceptor] 特性来标记,在需要代理的方法中,使用 继承了 ActionAttribute 的特性来标记。
此方法是侵入式的,需要在编译前完成。
[Interceptor]
public class Test : ITest
{
    [Log] public virtual string A { get; set; }
    [Log]
    public virtual void MyMethod()
    {
        Console.WriteLine("运行中");
    }
}
注意的是,一个方法或属性只能设置一个拦截器。
2,如何创建代理类型
CZGL.AOP 有多种生成代理类型的方式,下面介绍简单的方式。
请预先创建如下代码:
    public class LogAttribute : ActionAttribute
    {
        public override void Before(AspectContext context)
        {
            Console.WriteLine("执行前");
        }
        public override object After(AspectContext context)
        {
            Console.WriteLine("执行后");
            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("构造函数没问题");
        }
        [Log]
        public virtual void MyMethod()
        {
            Console.WriteLine("运行中");
        }
    }
2.1 通过API直接创建
通过 CZGL.AOP 中的 AopInterceptor 类,你可以生成代理类型。
示例如下:
            ITest test1 = AopInterceptor.CreateProxyOfInterface<ITest, Test>();
            Test test2 = AopInterceptor.CreateProxyOfClass<Test>();
            test1.MyMethod();
            test2.MyMethod();
CreateProxyOfInterface 通过接口创建代理类型;CreateProxyOfClass 通过类创建代理类型;
默认调用的是无参构造函数。
2,创建代理类型
通过API
你可以参考源码解决方案
中的 ExampleConsole 项目。
如果要直接使用 AopInterceptor.CreateProxyOfInterface 和 AopInterceptor.CreateProxyOfClass 方法,通过接口或类来创建代理类型。
        ITest test1 = AopInterceptor.CreateProxyOfInterface<ITest, Test>();
        Test test2 = AopInterceptor.CreateProxyOfClass<Test>();
如果要指定实例化的构造函数,可以这样:
            // 指定构造函数
            test2 = AopInterceptor.CreateProxyOfClass<Test>("aaa", "bbb");
            test2.MyMethod();
通过 Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.DependencyInjection 是 .NET Core/ASP.NET Core 默认的依赖注入容器。
如果需要支持 ASP.NET Core 中使用 AOP,你可以在 Nuget 包中安装 CZGL.AOP.MEDI。
如果你在控制台下使用 Microsoft.Extensions.DependencyInjection,你可以使用名为 BuildAopProxy 的 IServiceCollection 拓展方法来为容器中的类型,生成代理类型。
示例如下:
            IServiceCollection _services = new ServiceCollection();
            _services.AddTransient<ITest, Test>();
            var serviceProvider = _services.BuildAopProxy().BuildServiceProvider();
            serviceProvider.GetService<ITest>();
            return serviceProvider;
你可以参考源码解决方案中的 ExampleMEDI 项目。
如果你要在 ASP.NET Core 中使用,你可以在 Startup 中,ConfigureServices 方法的最后一行代码使用 services.BuildAopProxy(); 。
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.BuildAopProxy();
        }
还可以在 Program 的 IHostBuilder 中使用             .UseServiceProviderFactory(new AOPServiceProxviderFactory()) 来配置使用 CZGL.AOP。
示例:
        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
            .UseServiceProviderFactory(new AOPServiceProxviderFactory())
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
可以参考解决方案中的 ExampleConsole 和 ExampleWebMEDI 两个项目。
你不必担心引入 CZGL.AOP 后,使用依赖注入会使程序变慢或者破坏容器中的原有属性。CZGL.AOP 只会在创建容器时处理需要被代理的类型,不会影响容器中的服务,也不会干扰到依赖注入的执行。
通过 Autofac
如果需要在 Autofac 中使用 AOP,则需要引用 CZGL.AOP.Autofac 包。
如果你在控制台程序中使用 Autofac,则可以在 Build() 后面使用 BuildAopProxy()。
            ContainerBuilder builder = new ContainerBuilder();
            builder.RegisterType<Test>().As<ITest>();
            var container = builder.Build().BuildAopProxy();
            using (ILifetimeScope scope = container.BeginLifetimeScope())
            {
                // 获取实例
                ITest myService = scope.Resolve<ITest>();
                myService.MyMethod();
            }
            Console.ReadKey();
        }
要注意的是,在已经完成的组件注册创建一个新的容器后,才能调用 BuildAopProxy() 方法,
这样针对一个新的容器你可以考虑是否需要对容器中的组件进行代理。
如果在 ASP.NET Core 中使用 Autofac,你需要在 Program 类的 IHostBuilder 中使用:
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
如果需要代理已经注册的组件,则将其替换为:
 .UseServiceProviderFactory(new CZGL.AOP.Autofac.AOPServiceProxviderFactory())
请参考 源码解决方案中的 ExampleAutofac 和 ExampleWebAutofac 两个项目。
3,深入使用
代理类型
要被代理的类型,需要使用 [Interceptor]来标记,例如:
    [Interceptor]
    public class Test : ITest
    {
    }
支持泛型类型。
被代理的类型必须是可被继承的。
类型的构造函数没有限制,你可以随意编写。
在使用 API 创建代理类型并且实例化时,你可以指定使用哪个构造函数。
例如:
			string a="",b="",c="";
			ITest test1 = AopInterceptor.CreateProxyOfInterface<ITest, Test>(a,b,c);
API 会根据参数的多少以及参数的类型自动寻找合适的构造函数。
方法、属性代理
为了代理方法或属性,你需要继承 ActionAttribute 特性,然后为方法或属性标记此特性,并且将方法或属性设置为 virtual
一个类型中的不同方法,可以使用不同的拦截器。
        [Log1]
        public virtual void MyMethod1(){}
        [Log2]
        public virtual void MyMethod2(){}
对于属性,可以在属性上直接使用特性,或者只在 get 或 set 构造器使用。
        [Log] public virtual string A { get; set; }
        // 或
        public virtual string A { [Log] get; set; }
        // 或
        public virtual string A { get; [Log] set; }
如果在属性上使用特性,相当于 [Log] get; [Log] set;。
上下文
一个简单的方法或属性拦截标记是这样的:
    public class LogAttribute : ActionAttribute
    {
        public override void Before(AspectContext context)
        {
            Console.WriteLine("执行前");
        }
        public override object After(AspectContext context)
        {
            Console.WriteLine("执行后");
            if (context.IsMethod)
                return context.MethodResult;
            else if (context.IsProperty)
                return context.PropertyValue;
            return null;
        }
    }
AspectContext 的属性说明如下:
| 字段 | 说明 | 
|---|---|
| Type | 当前被代理类型生成的代理类型 | 
| ConstructorParamters | 类型被实例化时使用的构造函数的参数,如果构造函数没有参数,则 MethodValues.Length = 0,而不是 MethodValues 为 null。 | 
| IsProperty | 当前拦截的是属性 | 
| PropertyInfo | 当前被执行的属性的信息,可为 null。 | 
| PropertyValue | 但调用的是属性时,返回 get 的结果或 set 的 value 值。 | 
| IsMethod | 当前拦截的是方法 | 
| MethodInfo | 当前方法的信息 | 
| MethodValues | 方法被调用时传递的参数,如果此方法没有参数,则 MethodValues.Length = 0,而不是 MethodValues 为 null | 
| MethodResult | 方法执行返回的结果(如果有) | 
拦截方法或属性的参数
通过上下文,你可以修改方法或属性的参数以及拦截返回结果:
    public class LogAttribute : ActionAttribute
    {
        public override void Before(AspectContext context)
        {
            // 拦截并修改方法的参数
            for (int i = 0; i < context.MethodValues.Length; i++)
            {
                context.MethodValues[i] = (int)context.MethodValues[i] + 1;
            }
            Console.WriteLine("执行前");
        }
        public override object After(AspectContext context)
        {
            Console.WriteLine("执行后");
            // 拦截方法的执行结果
            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("运行中");
            return a + b;
        }
    }
            Test test = AopInterceptor.CreateProxyOfClass<Test>();
            Console.WriteLine(test.Sum(1, 1));
方法的参数支持 in、ref、out;支持泛型方法泛型属性。
非侵入式代理
此种方式不需要改动被代理的类型,你也可以代理程序集中的类型。
    public class LogAttribute : ActionAttribute
    {
        public override void Before(AspectContext context)
        {
            Console.WriteLine("执行前");
        }
        public override object After(AspectContext context)
        {
            Console.WriteLine("执行后");
            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("运行中");
        }
    }
            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()));
通过 ProxyTypeBuilder 来构建代理类型。
代理方法或属性都是使用 AddProxyMethod,第一个参数是要使用的拦截器,第二个参数是要拦截的方法。
如果要拦截属性,请分开设置属性的 get、set 构造。
如果多个方法或属性使用同一个拦截器,则可以这样:
            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()));
在构造函数中传递过去所需要的拦截器,然后在拦截时使用。
菜渣开源一个基于 EMIT 的 AOP 库(.NET Core)的更多相关文章
- Axios 是一个基于 promise 的 HTTP 库
		Axios 是一个基于 promise 的 HTTP 库 vue项目中关于axios的简单使用 axios介绍 Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.j ... 
- 手把手做一个基于vue-cli的组件库(上篇)
		基于vue-cli4的ui组件库,先贴个最终效果吧,步骤有点多,准备分上下篇,上篇:如何做一个初步的组件.下篇:编写说明文档及页面优化.开工. GitHub源码地址:https://github.co ... 
- 使用webpack4搭建一个基于Vue的组件库
		组内负责的几个项目都有一些一样的公共组件,所以就着手搭建了个公共组件开发脚手架,第一次开发 library,所以是参考着 iview 的配置来搭建的.记录如何使用webpack4搭建一个library ... 
- 开源一个基于天天团购的团购app
		可能大家都知道天天团购开源系统,一个做团购的开源项目很赞,前些日子做了基于天天团购系统做的团购客户端和移动端服务器!源代码放出,有了解的可以看看,希望收益! 先说服务器:app的服务器,基于天天团购的 ... 
- 开源一个基于dotnet standard的轻量级的ORM框架-Light.Data
		还在dotnet framework 2.0的时代,当时还没有EF,而NHibernate之类的又太复杂,并且自己也有一些特殊需求,如查询结果直接入表.水平分表和新增数据默认值等,就试着折腾个轻量点O ... 
- C# 开源一个基于 yarp 的 API 网关 Demo,支持绑定 Kubernetes Service
		关于 Neting 刚开始的时候是打算使用微软官方的 Yarp 库,实现一个 API 网关,后面发现坑比较多,弄起来比较麻烦,就放弃了.目前写完了查看 Kubernetes Service 信息.创建 ... 
- [安卓] 12、开源一个基于SurfaceView的飞行射击类小游戏
		前言 这款安卓小游戏是基于SurfaceView的飞行射击类游戏,采用Java来写,没有采用游戏引擎,注释详细,条理比较清晰,适合初学者了解游戏状态转化自动机和一些继承与封装的技巧. 效果展示 ... 
- 开源一个基于nio的java网络程序
		因为最近要从公司离职,害怕用nio写的网络程序没有人能看懂(或许是因为写的不好吧),就调整成了mina(这样大家接触起来非常方便,即使没有socket基础,用起来也不难),所以之前基于nio写的网络程 ... 
- 欢迎阅读daxnet的新博客:一个基于Microsoft Azure、ASP.NET Core和Docker的博客系统
		2008年11月,我在博客园开通了个人帐号,并在博客园发表了自己的第一篇博客.当然,我写博客也不是从2008年才开始的,在更早时候,也在CSDN和系统分析员协会(之后名为"希赛网" ... 
随机推荐
- 《HelloGitHub》第 50 期
			兴趣是最好的老师,HelloGitHub 就是帮你找到兴趣! 简介 分享 GitHub 上有趣.入门级的开源项目. 这是一个面向编程新手.热爱编程.对开源社区感兴趣 人群的月刊,月刊的内容包括:各种编 ... 
- 没找到工作的Java软件工程师是屌丝中的屌丝啊
			Java软件开发的工作咋就那么难找呢? 
- C#中的TemplateMethod模式
			一个真实的故事 大学的时候就开过一门课程,讲设计模式,可是大学生没什么编程实践经验,在大学里面听设计模式的感觉,就像听天书.听着都有道理,可是完全领会不到其中的奥妙,大抵原因就在于没有走过弯路,没有吃 ... 
- elasticsearch中保存时间格式
			利用logstash从文档中导入数据到es中,若未事先设定数据格式,有可能存储时间并未保存为date格式而是text格式. 时间若保存为text,则在会以字符串数组格式存储在es中,是乱序,不好查询. ... 
- Java实现 蓝桥杯VIP 算法训练 友好数
			有两个整数,如果每个整数的约数和(除了它本身以外)等于对方,我们就称这对数是友好的.例如: 9的约数和有:1+3=4 4的约数和有:1+2=3 所以9和4不是友好的. 220的约数和有:1 2 4 5 ... 
- Java实现 LeetCode 284 顶端迭代器
			284. 顶端迭代器 给定一个迭代器类的接口,接口包含两个方法: next() 和 hasNext().设计并实现一个支持 peek() 操作的顶端迭代器 – 其本质就是把原本应由 next() 方法 ... 
- Java实现 LeetCode 96 不同的二叉搜索树
			96. 不同的二叉搜索树 给定一个整数 n,求以 1 - n 为节点组成的二叉搜索树有多少种? 示例: 输入: 3 输出: 5 解释: 给定 n = 3, 一共有 5 种不同结构的二叉搜索树: 1 3 ... 
- Java实现 LeetCode 6 Z字形变换
			6. Z 字形变换 将一个给定字符串根据给定的行数,以从上往下.从左到右进行 Z 字形排列. 比如输入字符串为 "LEETCODEISHIRING" 行数为 3 时,排列如下: L ... 
- java实现放麦子问题
			/* 你一定听说过这个故事.国王对发明国际象棋的大臣很佩服, 问他要什么报酬,大臣说:请在第 1 个棋盘格放 1 粒麦子, 在第 2 个棋盘格放 2 粒麦子,在第 3 个棋盘格放 4 粒麦子, 在第 ... 
- java实现第六届蓝桥杯灾后重建
			灾后重建 题目描述 Pear市一共有N(<=50000)个居民点,居民点之间有M(<=200000)条双向道路相连.这些居民点两两之间都可以通过双向道路到达.这种情况一直持续到最近,一次严 ... 
