什么是 AOP ?
AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,旨在通过将横切关注点(cross-cutting concerns)从主要业务逻辑中分离出来,以提高代码的模块化性、可维护性和复用性。
在传统的面向对象编程中,我们通常通过类和对象来组织和实现功能。然而,某些功能,如日志记录、事务管理、安全性检查等,可能会跨越多个对象和模块,这种跨越称为横切关注点。AOP 的核心思想是将这些横切关注点从业务逻辑中分离出来,通过特定的机制将它们应用到代码中,而不是通过直接修改业务逻辑来实现。
.Net Core 中 有哪些 AOP 框架?
PostSharp(收费)
PostSharp是一个功能强大的AOP框架,它通过编译器插件的形式集成到Visual Studio中。PostSharp支持编译时AOP(通过C#特性应用切面),并提供了丰富的切面类型,包括方法拦截、属性访问拦截、异常处理等。它还提供了商业支持和丰富的文档。
Castle DynamicProxy
Castle DynamicProxy是Castle项目的一部分,它允许开发者在运行时动态创建代理类,这些代理类可以拦截对目标对象的调用,并在调用前后执行自定义逻辑。虽然它本身不是一个完整的AOP框架,但它经常被用作构建AOP解决方案的基础。
AspectCore Framework
AspectCore 是一个开源的 AOP 框架,专为 .NET Core 设计。它提供了基于动态代理的运行时切面和方法拦截机制,支持常见的切面编程需求,如日志、缓存、事务等。
基于 Castle DynamicProxy 实现 AOP
1. 安装Castle.Core NuGet包
Install-Package Castle.Core
2. 定义接口和类
假设你有一个接口和一个实现了该接口的类,你想要拦截这个类的方法调用。
public interface IMyService { void PerformAction(); } public class MyService : IMyService { public void PerformAction() { Console.WriteLine("Action performed."); } }
3. 创建拦截器
接下来,你需要创建一个拦截器类,该类将实现IInterceptor接口。在这个接口的实现中,你可以定义在调用目标方法之前和之后要执行的逻辑。
using Castle.DynamicProxy; public class MyInterceptor : IInterceptor { public void Intercept(IInvocation invocation) { // 在调用目标方法之前执行的逻辑 Console.WriteLine("Before method: " + invocation.Method.Name); // 调用目标方法 invocation.Proceed(); // 在调用目标方法之后执行的逻辑 Console.WriteLine("After method: " + invocation.Method.Name); } }
4. 创建代理并调用方法
最后,你需要使用ProxyGenerator类来创建MyService的代理实例,并指定拦截器。然后,你可以像使用普通对象一样调用代理的方法,但拦截器中的逻辑会在调用发生时执行。
using Castle.DynamicProxy; public class Program { public static void Main(string[] args) { var generator = new ProxyGenerator(); var interceptor = new MyInterceptor(); // 创建MyService的代理实例,并指定拦截器 var proxy = generator.CreateInterfaceProxyWithTarget<IMyService>( new MyService(), interceptor); // 调用代理的方法,拦截器中的逻辑将被执行 proxy.PerformAction(); } }
注意,上面的示例使用了接口代理(CreateInterfaceProxyWithTarget),这意味着你的目标类必须实现一个或多个接口。如果你想要代理一个类而不是接口,你可以使用CreateClassProxyWithTarget方法(但这通常用于需要代理非虚方法或字段的场景,且要求目标类是可继承的)。
IOC中使用 Castle DynamicProxy
由于IOC容器(如Microsoft的IServiceCollection和IServiceProvider)通常不直接支持AOP,所以用 Autofac
1. 安装必要的 NuGet 包
首先,确保你的项目中安装了以下 NuGet 包:
Install-Package Autofac Install-Package Autofac.Extensions.DependencyInjection Install-Package Autofac.Extras.DynamicProxy Install-Package Castle.Core
2. 创建服务接口和实现类
public class User { public long Id { get; set; } public string Name { get; set; } public long CreateUserId { get; set; } public string CreateUserName { get; set; } public DateTime CreateTime { get; set; } public long UpdateUserId { get; set; } public string UpdateUserName { get; set; } public DateTime UpdateTime { get; set; } } public interface IUserService { void Test(); Task<int> TaskTest(); void Add(User user); void Update(User user); } public class UserService : IUserService { public void Test() { Console.WriteLine("Test"); } public async Task<int> TaskTest() { await Console.Out.WriteLineAsync("TaskTest"); return 1; } public void Add(User user) { Console.WriteLine(user.CreateUserId); Console.WriteLine(user.CreateUserName); } public void Update(User user) { Console.WriteLine(user.UpdateUserId); Console.WriteLine(user.UpdateUserName); } } [ApiController] [Route("[controller]")] public class UserController : ControllerBase { readonly IUserService _userService; public UserController(IUserService userService) { _userService = userService; } [HttpGet] [Route("/taskTest")] public async Task<string> TaskTest() { await _userService.TaskTest(); return "ok"; } [HttpGet] [Route("/test")] public string Test() { _userService.Test(); return "ok"; } [HttpGet] [Route("/add")] public string Add() { _userService.Add(new Model.User { Name = "张三" }); return "ok"; } [HttpGet] [Route("/update")] public string Update() { _userService.Update(new Model.User { Name = "张三" }); return "ok"; } }
3. 创建拦截器类
创建一个实现 IInterceptor 接口的拦截器类 LoggingInterceptor,用于拦截方法调用并添加日志记录:
public class LoggingInterceptor : IInterceptor { public void Intercept(IInvocation invocation) { Console.WriteLine($"Before executing: {invocation.Method.Name}"); invocation.Proceed(); // 调用原始方法 Console.WriteLine($"After executing: {invocation.Method.Name}"); } }
4. 配置 Autofac 容器
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()) //使用Autofac .ConfigureContainer<ContainerBuilder>(autofacBuilder => { autofacBuilder.RegisterType<LoggingInterceptor>(); autofacBuilder.RegisterType<UserService>().As<IUserService> ().SingleInstance().AsImplementedInterfaces() .EnableInterfaceInterceptors() // 启用接口拦截器 .InterceptedBy(typeof(LoggingInterceptor)); //指定拦截器 });
与Autofac集成时,配置拦截器主要有两种方式
使用 InterceptAttribute 特性
这种方式通过在接口或类上添加[Intercept(typeof(YourInterceptor))]特性来指定拦截器。然后,在Autofac注册时,启用接口或类的拦截器。(通常不推荐在类上直接添加,因为这会使类与Autofac紧密耦合)
[Intercept(typeof(UserAutoFillInterceptor))] public class UserService : IUserService { public void Test() { Console.WriteLine("Test"); } } autofacBuilder.RegisterType<UserService>().As<IUserService>().EnableInterfaceInterceptors() // 启用接口拦截器
使用 InterceptedBy() 方法
这种方式不依赖于[Intercept]特性,而是在注册服务时直接使用InterceptedBy()方法来指定拦截器。
autofacBuilder.RegisterType<UserService>().As<IUserService>() .EnableInterfaceInterceptors() // 启用接口拦截器 .InterceptedBy(typeof(LoggingInterceptor)); //指定拦截器
实现事务管理
拦截器基类
/// <summary> /// 拦截基类 /// </summary> /// <typeparam name="T"></typeparam> public abstract class BaseInterceptor<T> : IInterceptor { protected readonly ILogger<T> _logger; public BaseInterceptor(ILogger<T> logger) { _logger = logger; } /// <summary> /// 拦截方法 /// </summary> /// <param name="invocation"></param> public virtual void Intercept(IInvocation invocation) { try { Method = invocation.MethodInvocationTarget ?? invocation.Method; InterceptHandle(invocation); } catch (Exception ex) { _logger.LogError(ex, ex.Message); throw ex; } } /// <summary> /// 拦截处理 /// </summary> /// <param name="invocation"></param> public abstract void InterceptHandle(IInvocation invocation); protected MethodInfo Method{ get; set; } public static bool IsAsyncMethod(MethodInfo method) { return (method.ReturnType == typeof(Task) || (method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)) ); } }
事务特性:用来判断是否需要事务管理的
/// <summary> /// 事务特性 /// </summary> [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true)] public class TransactionalAttribute : Attribute { public TransactionalAttribute() { Timeout = 60; } /// <summary> /// /// </summary> public int Timeout { get; set; } /// <summary> /// 事务隔离级别 /// </summary> public IsolationLevel IsolationLevel { get; set; } /// <summary> /// 事务传播方式 /// </summary> public Propagation Propagation { get; set; } } /// <summary> /// 事务传播方式 /// </summary> public enum Propagation { /// <summary> /// 默认:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中。 /// </summary> Required = 0, /// <summary> /// 使用当前事务,如果没有当前事务,就抛出异常 /// </summary> Mandatory = 1, /// <summary> /// 以嵌套事务方式执行 /// </summary> Nested = 2, }
事务拦截器:处理事务的
/// <summary> /// 事务拦截器 /// </summary> public class TransactionalInterceptor : BaseInterceptor<TransactionalInterceptor> { public TransactionalInterceptor(ILogger<TransactionalInterceptor> logger) : base(logger) { } public override void InterceptHandle(IInvocation invocation) { if (Method.GetCustomAttribute<TransactionalAttribute>(true) == null && Method.DeclaringType?.GetCustomAttribute<TransactionalAttribute>(true) == null) { invocation.Proceed(); } else { try { Console.WriteLine("开启事务"); //执行方法 invocation.Proceed(); // 异步获取异常,先执行 if (IsAsyncMethod(invocation.Method)) { var result = invocation.ReturnValue; if (result is Task) { Task.WaitAll(result as Task); } } Console.WriteLine("提交事务"); } catch (Exception ex) { Console.WriteLine("回滚事务"); _logger.LogError(ex, ex.Message); throw ex; } } } }
接口上加入事务特性
//[Transactional] public class UserService : IUserService { [Transactional] public void Test() { Console.WriteLine("Test"); } }
注入IOC
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()) .ConfigureContainer<ContainerBuilder>(autofacBuilder => { autofacBuilder.RegisterType<TransactionalInterceptor>(); autofacBuilder.RegisterType<UserService>().As<IUserService>().SingleInstance().AsImplementedInterfaces() .EnableInterfaceInterceptors() .InterceptedBy(typeof(TransactionalInterceptor)); });
测试

实现用户自动填充
上下户用户
public interface IHttpContextUser { long UserId { get; } string UserName { get;} } public class HttpContextUser : IHttpContextUser { private readonly IHttpContextAccessor _accessor; public HttpContextUser(IHttpContextAccessor accessor) { _accessor = accessor; } public long UserId { get { return 1; //这里暂时是写死的 if (int.TryParse(_accessor.HttpContext?.User?.FindFirstValue(ClaimTypes.Sid), out var userId)) { return userId; } return default; } } public string UserName { get { return "admin"; //这里暂时是写死的 return _accessor.HttpContext?.User?.FindFirstValue(ClaimTypes.Name) ?? ""; } } }
注入IOC
builder.Services.AddHttpContextAccessor(); builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()) .ConfigureContainer<ContainerBuilder>(autofacBuilder => { autofacBuilder.RegisterType<HttpContextUser>().As<IHttpContextUser>().SingleInstance().AsImplementedInterfaces(); autofacBuilder.RegisterType<UserAutoFillInterceptor>(); autofacBuilder.RegisterType<UserService>().As<IUserService>().SingleInstance().AsImplementedInterfaces() .EnableInterfaceInterceptors() .InterceptedBy(typeof(UserAutoFillInterceptor)); });
用户自动拦截器:处理用户填充的
/// <summary> /// 用户自动填充拦截器 /// </summary> public class UserAutoFillInterceptor : BaseInterceptor<UserAutoFillInterceptor> { private readonly IHttpContextUser _user; public UserAutoFillInterceptor(ILogger<UserAutoFillInterceptor> logger,IHttpContextUser user) : base(logger) { _user = user; } public override void InterceptHandle(IInvocation invocation) { //对当前方法的特性验证 if (Method.Name?.ToLower() == "add" || Method.Name?.ToLower() == "update") { if (invocation.Arguments.Length == 1 && invocation.Arguments[0].GetType().IsClass) { dynamic argModel = invocation.Arguments[0]; var getType = argModel.GetType(); if (Method.Name?.ToLower() == "add") { if (getType.GetProperty("CreateUserId") != null) { argModel.CreateUserId = _user.UserId; } if (getType.GetProperty("CreateUserName") != null) { argModel.CreateUserName = _user.UserName; } if (getType.GetProperty("CreateTime") != null) { argModel.CreateTime = DateTime.Now; } } if (getType.GetProperty("UpdateUserId") != null) { argModel.UpdateUserId = _user.UserId; } if (getType.GetProperty("UpdateUserName") != null) { argModel.UpdateUserName = _user.UserName; } if (getType.GetProperty("UpdateTime") != null) { argModel.UpdateTime = DateTime.Now; } } invocation.Proceed(); } else { invocation.Proceed(); } } }
测试
