文章目录
- 什么是 AOP ?
- .Net Core 中 有哪些 AOP 框架?
- 基于 Castle DynamicProxy 实现 AOP
- IOC中使用 Castle DynamicProxy
- 实现事务管理
- 实现用户自动填充
什么是 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();}}}
测试