阅读本文你的收获:
- 学习AOP的另外一种实现方式——拦截器
- 了解拦截器和过滤器的区别
- 手把手教你写一个拦截器实现事务管理
引言
上一篇文章ASP.NET Core基础之AOP编程(一)-过滤器中,我们介绍了AOP思想的其中一种实现方式——过滤器。这次我们来学习另外一种实现AOP思想的方式:拦截器。
一. 拦截器(Interceptors)的应用场景?
应用场景有:
- 拦截和修改请求和响应数据
- 限制请求速率和并发连接数
- 实现缓存和本地存储
- 在请求处理前进行数据验证和格式化
- 在请求处理后进行数据处理和清理
- 数据库事务处理
过滤器和拦截器的区别:
- 过滤器是对控制器Controller/Action的AOP的实现;
- 拦截器,拦截的是注册在IOC容器里的服务实例里的方法;
- 过滤器和拦截器 底层实现方式大不相同,
过滤器
是基于函数回调的,拦截器
则是基于动态代理机制实现的。
二. Autofac与拦截器
Autofac是一个.NET下非常优秀,性能非常好的IOC容器(.NET下效率最高的容器),加上AOP简直是如虎添翼。
Autofac的AOP是通过大名鼎鼎的Castle(也是一个容器)项目的Castle.Core来实现的,名为Autofac.Extras.DynamicProxy,顾名思义,其实现方式为动态代理(DynamicProxy)。
什么是动态代理?
动态代理是代理模式的一种实现方式,代理模式除了动态代理还有 静态代理,只不过静态代理能够在编译时期确定类的执行对象,而动态代理只有在运行时才能够确定执行对象是谁。代理可以看作是对最终调用目标的一个封装,我们能够通过操作代理对象来调用目标类,这样就可以实现调用者和目标对象的解耦合。
一般我们获取一个类型的实例都是通过 new 关键字,例如 var c = new Class1(); 通过动态代理的话,我们获取一个实例是通过代理方法获取的,generator.CreateClassProxy(type, interceptor); 其中Type是要代理的类型,也就是Class1(); interceptor则是实际执行代理的拦截器。
三. 用拦截器实现AOP编程
以下演示用拦截器实现事务AOP,开发环境:
平台版本是:.NET6
开发框架:ASP.NET Core WebApi
开发工具:Visual Studio 2022
3.1 实现事务拦截器
-
编写自定义事务拦截器,在这之前我们先定义一个UseTranAttribute;
/// <summary> /// 这个Attribute就是使用时候的验证,把它添加到需要执行事务的方法中,即可完成事务的操作。 /// </summary> [AttributeUsage(AttributeTargets.Method, Inherited = true)] public class UseTranAttribute : Attribute { }
/// <summary> /// 数据库事务拦截器 继承Castle.DynamicProxy.IInterceptor接口 /// </summary> public class DbTranInterceptor : IInterceptor { //记录日志 private readonly ILogger<DbTranInterceptor> _logger; //工作单元对象 private readonly IUnitOfWorkManage _unitOfWorkManage; public DbTranInterceptor(IUnitOfWorkManage unitOfWorkManage, ILogger<DbTranInterceptor> logger) { _unitOfWorkManage = unitOfWorkManage; _logger = logger; } /// <summary> /// 实例化IInterceptor唯一方法 /// </summary> /// <param name="invocation">包含被拦截方法的信息</param> public void Intercept(IInvocation invocation) { //获取被拦截的方法对象 var method = invocation.MethodInvocationTarget ?? invocation.Method; //检查当前方法是否有[UseTran]特性修饰 if (method.GetCustomAttribute<UseTranAttribute>(true) is { } uta) { try { _unitOfWorkManage.BeginTran(); //开启事务 invocation.Proceed(); //判断方法是否为异步方法,异步方法则需要Wait等待 if (IsAsyncMethod(invocation.Method)) { var result = invocation.ReturnValue; if (result is Task) { Task.WaitAll(result as Task); } } _unitOfWorkManage.CommitTran(); //提交事务 } catch (Exception ex) { _logger.LogError(ex.ToString()); _unitOfWorkManage.RollbackTran(); //回滚事务 throw; } } else { invocation.Proceed(); //如果方法没有[UseTran]则不需要开启事务,直接执行被拦截方法 } } /// <summary> /// 判断方法是否为异步方法 /// </summary> /// <param name="method"></param> /// <returns></returns> public static bool IsAsyncMethod(MethodInfo method) { return ( method.ReturnType == typeof(Task) || (method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)) ); } }
-
注册自定义事务拦截器
在Program.cs中,启用Autofac依赖注入容器,并完成拦截器注册。using Autofac; using Autofac.Extensions.DependencyInjection; using Autofac.Extras.DynamicProxy; //动态代理 builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()) .ConfigureContainer<ContainerBuilder>(builder => { //注册拦截器到Autofac容器 builder.RegisterType<DbTranInterceptor>(); //AutoFac批量注册所有Service类,并启用拦截器 builder.RegisterAssemblyTypes(Assembly.Load("OAWeb.Service")) .Where(t => t.Name.EndsWith("Service")) .AsImplementedInterfaces() .PropertiesAutowired() .EnableInterfaceInterceptors() //这里动态创建一个接口代理,另外还有一个 .InterceptedBy(typeof(DbTranInterceptor)); //动态注入拦截器DbTranInterceptor //AutoFac批量注册所有Repository仓储类 builder.RegisterAssemblyTypes(Assembly.Load("OAWeb.EFCore")) .Where(t => t.Name.EndsWith("Repository")) .AsImplementedInterfaces() .PropertiesAutowired() .InstancePerLifetimeScope(); //域模式 //AutoFac批量注册泛型仓储 builder.RegisterGeneric(typeof(EfBaseRepository<>)) .AsImplementedInterfaces() .InstancePerLifetimeScope(); });
-
测试自定义事务拦截器
要在服务层的方法上边加上[UseTran],则该方法中的代码会在一个事务中执行。public class UserService : IUserService { //完成必要的一些服务注入,略 //为用户添加角色 [UseTran] //使用事务的特性标注 public int AddRole(AddUserRoleDto input) { //为用户添加角色 } }
本次分享就这么多,AOP相关的基础入门到此篇就结束了,如果对你有帮助,欢迎点赞+评论+关注。