什么是 AOP ?
AOP(Aspect-Oriented Programming,面向切面編程)是一種編程范式,旨在通過將橫切關(guān)注點(cross-cutting concerns)從主要業(yè)務(wù)邏輯中分離出來,以提高代碼的模塊化性、可維護(hù)性和復(fù)用性。
在傳統(tǒng)的面向?qū)ο缶幊讨校覀兺ǔMㄟ^類和對象來組織和實現(xiàn)功能。然而,某些功能,如日志記錄、事務(wù)管理、安全性檢查等,可能會跨越多個對象和模塊,這種跨越稱為橫切關(guān)注點。AOP 的核心思想是將這些橫切關(guān)注點從業(yè)務(wù)邏輯中分離出來,通過特定的機制將它們應(yīng)用到代碼中,而不是通過直接修改業(yè)務(wù)邏輯來實現(xiàn)。
.Net Core 中 有哪些 AOP 框架?
PostSharp(收費)
PostSharp是一個功能強大的AOP框架,它通過編譯器插件的形式集成到Visual Studio中。PostSharp支持編譯時AOP(通過C#特性應(yīng)用切面),并提供了豐富的切面類型,包括方法攔截、屬性訪問攔截、異常處理等。它還提供了商業(yè)支持和豐富的文檔。
Castle DynamicProxy
Castle DynamicProxy是Castle項目的一部分,它允許開發(fā)者在運行時動態(tài)創(chuàng)建代理類,這些代理類可以攔截對目標(biāo)對象的調(diào)用,并在調(diào)用前后執(zhí)行自定義邏輯。雖然它本身不是一個完整的AOP框架,但它經(jīng)常被用作構(gòu)建AOP解決方案的基礎(chǔ)。
AspectCore Framework
AspectCore 是一個開源的 AOP 框架,專為 .NET Core 設(shè)計。它提供了基于動態(tài)代理的運行時切面和方法攔截機制,支持常見的切面編程需求,如日志、緩存、事務(wù)等。
基于 Castle DynamicProxy 實現(xiàn) AOP
1. 安裝Castle.Core NuGet包
Install-Package Castle.Core
2. 定義接口和類
假設(shè)你有一個接口和一個實現(xiàn)了該接口的類,你想要攔截這個類的方法調(diào)用。
public interface IMyService
{
void PerformAction();
}
public class MyService : IMyService
{
public void PerformAction()
{
Console.WriteLine("Action performed.");
}
}
3. 創(chuàng)建攔截器
接下來,你需要創(chuàng)建一個攔截器類,該類將實現(xiàn)IInterceptor
接口。在這個接口的實現(xiàn)中,你可以定義在調(diào)用目標(biāo)方法之前和之后要執(zhí)行的邏輯。
using Castle.DynamicProxy;
public class MyInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
// 在調(diào)用目標(biāo)方法之前執(zhí)行的邏輯
Console.WriteLine("Before method: " + invocation.Method.Name);
// 調(diào)用目標(biāo)方法
invocation.Proceed();
// 在調(diào)用目標(biāo)方法之后執(zhí)行的邏輯
Console.WriteLine("After method: " + invocation.Method.Name);
}
}
4. 創(chuàng)建代理并調(diào)用方法
最后,你需要使用ProxyGenerator
類來創(chuàng)建MyService
的代理實例,并指定攔截器。然后,你可以像使用普通對象一樣調(diào)用代理的方法,但攔截器中的邏輯會在調(diào)用發(fā)生時執(zhí)行。
using Castle.DynamicProxy;
public class Program
{
public static void Main(string[] args)
{
var generator = new ProxyGenerator();
var interceptor = new MyInterceptor();
// 創(chuàng)建MyService的代理實例,并指定攔截器
var proxy = generator.CreateInterfaceProxyWithTarget<IMyService>(
new MyService(), interceptor);
// 調(diào)用代理的方法,攔截器中的邏輯將被執(zhí)行
proxy.PerformAction();
}
}
注意,上面的示例使用了接口代理(CreateInterfaceProxyWithTarget
),這意味著你的目標(biāo)類必須實現(xiàn)一個或多個接口。如果你想要代理一個類而不是接口,你可以使用CreateClassProxyWithTarget
方法(但這通常用于需要代理非虛方法或字段的場景,且要求目標(biāo)類是可繼承的)。
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. 創(chuàng)建服務(wù)接口和實現(xiàn)類
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. 創(chuàng)建攔截器類
創(chuàng)建一個實現(xiàn) IInterceptor
接口的攔截器類 LoggingInterceptor
,用于攔截方法調(diào)用并添加日志記錄:
public class LoggingInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.WriteLine($"Before executing: {invocation.Method.Name}");
invocation.Proceed(); // 調(diào)用原始方法
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]
特性,而是在注冊服務(wù)時直接使用InterceptedBy()
方法來指定攔截器。
autofacBuilder.RegisterType<UserService>().As<IUserService>()
.EnableInterfaceInterceptors() // 啟用接口攔截器
.InterceptedBy(typeof(LoggingInterceptor)); //指定攔截器
實現(xiàn)事務(wù)管理
攔截器基類
/// <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<>))
);
}
}
事務(wù)特性:用來判斷是否需要事務(wù)管理的
/// <summary>
/// 事務(wù)特性
/// </summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true)]
public class TransactionalAttribute : Attribute
{
public TransactionalAttribute()
{
Timeout = 60;
}
/// <summary>
///
/// </summary>
public int Timeout { get; set; }
/// <summary>
/// 事務(wù)隔離級別
/// </summary>
public IsolationLevel IsolationLevel { get; set; }
/// <summary>
/// 事務(wù)傳播方式
/// </summary>
public Propagation Propagation { get; set; }
}
/// <summary>
/// 事務(wù)傳播方式
/// </summary>
public enum Propagation
{
/// <summary>
/// 默認(rèn):如果當(dāng)前沒有事務(wù),就新建一個事務(wù),如果已存在一個事務(wù)中,加入到這個事務(wù)中。
/// </summary>
Required = 0,
/// <summary>
/// 使用當(dāng)前事務(wù),如果沒有當(dāng)前事務(wù),就拋出異常
/// </summary>
Mandatory = 1,
/// <summary>
/// 以嵌套事務(wù)方式執(zhí)行
/// </summary>
Nested = 2,
}
事務(wù)攔截器:處理事務(wù)的
/// <summary>
/// 事務(wù)攔截器
/// </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("開啟事務(wù)");
//執(zhí)行方法
invocation.Proceed();
// 異步獲取異常,先執(zhí)行
if (IsAsyncMethod(invocation.Method))
{
var result = invocation.ReturnValue;
if (result is Task)
{
Task.WaitAll(result as Task);
}
}
Console.WriteLine("提交事務(wù)");
}
catch (Exception ex)
{
Console.WriteLine("回滾事務(wù)");
_logger.LogError(ex, ex.Message);
throw ex;
}
}
}
}
接口上加入事務(wù)特性
//[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));
});
測試
實現(xiàn)用戶自動填充
上下戶用戶
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)
{
//對當(dāng)前方法的特性驗證
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();
}
}
}
測試
該文章在 2024/9/13 9:24:52 編輯過