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.CreateProxyOfInterfaceAopInterceptor.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,你可以使用名为 BuildAopProxyIServiceCollection 拓展方法来为容器中的类型,生成代理类型。

示例如下:

            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();
}

还可以在 ProgramIHostBuilder 中使用 .UseServiceProviderFactory(new AOPServiceProxviderFactory()) 来配置使用 CZGL.AOP。

示例:

        public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AOPServiceProxviderFactory())
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});

可以参考解决方案中的 ExampleConsoleExampleWebMEDI 两个项目。

你不必担心引入 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())

请参考 源码解决方案中的 ExampleAutofacExampleWebAutofac 两个项目。

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));

方法的参数支持 inrefout;支持泛型方法泛型属性。

非侵入式代理

此种方式不需要改动被代理的类型,你也可以代理程序集中的类型。

    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,第一个参数是要使用的拦截器,第二个参数是要拦截的方法。

如果要拦截属性,请分开设置属性的 getset 构造。

如果多个方法或属性使用同一个拦截器,则可以这样:

            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)的更多相关文章

  1. Axios 是一个基于 promise 的 HTTP 库

    Axios 是一个基于 promise 的 HTTP 库 vue项目中关于axios的简单使用 axios介绍 Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.j ...

  2. 手把手做一个基于vue-cli的组件库(上篇)

    基于vue-cli4的ui组件库,先贴个最终效果吧,步骤有点多,准备分上下篇,上篇:如何做一个初步的组件.下篇:编写说明文档及页面优化.开工. GitHub源码地址:https://github.co ...

  3. 使用webpack4搭建一个基于Vue的组件库

    组内负责的几个项目都有一些一样的公共组件,所以就着手搭建了个公共组件开发脚手架,第一次开发 library,所以是参考着 iview 的配置来搭建的.记录如何使用webpack4搭建一个library ...

  4. 开源一个基于天天团购的团购app

    可能大家都知道天天团购开源系统,一个做团购的开源项目很赞,前些日子做了基于天天团购系统做的团购客户端和移动端服务器!源代码放出,有了解的可以看看,希望收益! 先说服务器:app的服务器,基于天天团购的 ...

  5. 开源一个基于dotnet standard的轻量级的ORM框架-Light.Data

    还在dotnet framework 2.0的时代,当时还没有EF,而NHibernate之类的又太复杂,并且自己也有一些特殊需求,如查询结果直接入表.水平分表和新增数据默认值等,就试着折腾个轻量点O ...

  6. C# 开源一个基于 yarp 的 API 网关 Demo,支持绑定 Kubernetes Service

    关于 Neting 刚开始的时候是打算使用微软官方的 Yarp 库,实现一个 API 网关,后面发现坑比较多,弄起来比较麻烦,就放弃了.目前写完了查看 Kubernetes Service 信息.创建 ...

  7. [安卓] 12、开源一个基于SurfaceView的飞行射击类小游戏

    前言  这款安卓小游戏是基于SurfaceView的飞行射击类游戏,采用Java来写,没有采用游戏引擎,注释详细,条理比较清晰,适合初学者了解游戏状态转化自动机和一些继承与封装的技巧. 效果展示    ...

  8. 开源一个基于nio的java网络程序

    因为最近要从公司离职,害怕用nio写的网络程序没有人能看懂(或许是因为写的不好吧),就调整成了mina(这样大家接触起来非常方便,即使没有socket基础,用起来也不难),所以之前基于nio写的网络程 ...

  9. 欢迎阅读daxnet的新博客:一个基于Microsoft Azure、ASP.NET Core和Docker的博客系统

    2008年11月,我在博客园开通了个人帐号,并在博客园发表了自己的第一篇博客.当然,我写博客也不是从2008年才开始的,在更早时候,也在CSDN和系统分析员协会(之后名为"希赛网" ...

随机推荐

  1. 关于 conda中的 lxml 无法导入 etree 问题

    找到你conda的安装目录下\Lib\site-packages下的两个文件夹lxml和lxml-4.3.4.dist-info,将这两个文件夹删除. 执行命令pip install lxml 重新安 ...

  2. MySql轻松入门系列————第一站 从源码角度轻松认识mysql整体框架图

    一:背景 1. 讲故事 最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlser ...

  3. Rocket - diplomacy - DUEB参数模型分析

    https://mp.weixin.qq.com/s/533bJxcPRgO4W2gf_OEhEw   分析DUEB参数模型中各种参数类型的可能性.     1. 节点类型   根据参数的传播方向,可 ...

  4. Java中IO软件包的详细介绍

    一.Java Io流 Java Io流的概念 java的io是实现输入和输出的基础,可以方便的实现数据的输入和输出操作.在java中把不同的输入/输出源(键盘,文件,网络连接等)抽象表述为" ...

  5. java实现求二十一位水仙花数(21位水仙花数)

    一个N位的十进制正整数,如果它的每个位上的数字的N次方的和等于这个数本身,则称其为花朵数. 例如: 当N=3时,153就满足条件,因为 1^3 + 5^3 + 3^3 = 153,这样的数字也被称为水 ...

  6. java实现第六届蓝桥杯加法变乘法

    加法变乘法 题目描述 我们都知道:1+2+3+ - + 49 = 1225 现在要求你把其中两个不相邻的加号变成乘号,使得结果为2015 比如: 1+2+3+-+1011+12+-+2728+29+- ...

  7. 原生js实现点击添加购物车按钮出现飞行物飞向购物车

    效果演示: 思路:核心->抛物线公式 let a = -((y2-y3)*x1 - (x2-x3)*y1 + x2*y3 - x3*y2) / ((x2-x3) * (x1-x2) * (x1- ...

  8. Tidyverse| XX_join :多个数据表(文件)之间的各种连接

    本文首发于公众号:“生信补给站” Tidyverse| XX_join :多个数据表(文件)之间的各种连接 前面分享了单个文件中的select列,filter行,列拆分等,实际中经常是多个数据表,综合 ...

  9. python自学Day05(自学书籍python编程从入门到实践)

    第6章 字典 6.1 一个简单的字典 先跟随书本创建一个简单的字典感受一下. alien_0 = {'color':'green','points':5} print(alien_0['color'] ...

  10. 【JAVA习题二十八】海滩上有一堆桃子,五只猴子来分。第一只猴子把这堆桃子凭据分为五份,多了一个,这只猴子把多的一个扔入海中,拿走了一份。第二只猴子把剩下的桃子又平均分成五份,又多了一个,它同样把多的一个扔入海中,拿走了一份,第三、第四、第五只猴子都是这样做的,问海滩上原来最少有多少个桃子?

    package erase; import java.util.Scanner; public class 猴子分桃 { public static void main(String[] args) ...