什么是 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(
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(autofacBuilder =>
{
autofacBuilder.RegisterType();
autofacBuilder.RegisterType().As ().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().As().EnableInterfaceInterceptors() // 启用接口拦截器
使用 InterceptedBy()
方法
这种方式不依赖于[Intercept]
特性,而是在注册服务时直接使用InterceptedBy()
方法来指定拦截器。
autofacBuilder.RegisterType().As()
.EnableInterfaceInterceptors() // 启用接口拦截器
.InterceptedBy(typeof(LoggingInterceptor)); //指定拦截器
实现事务管理
拦截器基类
///
/// 拦截基类
///
///
public abstract class BaseInterceptor<T> : IInterceptor
{
protected readonly ILogger _logger;
public BaseInterceptor(ILogger logger)
{
_logger = logger;
}
///
/// 拦截方法
///
///
public virtual void Intercept(IInvocation invocation)
{
try
{
Method = invocation.MethodInvocationTarget ?? invocation.Method;
InterceptHandle(invocation);
}
catch (Exception ex)
{
_logger.LogError(ex, ex.Message);
throw ex;
}
}
///
/// 拦截处理
///
///
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<>))
);
}
}
事务特性:用来判断是否需要事务管理的
///
/// 事务特性
///
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true)]
public class TransactionalAttribute : Attribute
{
public TransactionalAttribute()
{
Timeout = 60;
}
///
///
///
public int Timeout { get; set; }
///
/// 事务隔离级别
///
public IsolationLevel IsolationLevel { get; set; }
///
/// 事务传播方式
///
public Propagation Propagation { get; set; }
}
///
/// 事务传播方式
///
public enum Propagation
{
///
/// 默认:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中。
///
Required = 0,
///
/// 使用当前事务,如果没有当前事务,就抛出异常
///
Mandatory = 1,
///
/// 以嵌套事务方式执行
///
Nested = 2,
}
事务拦截器:处理事务的
///
/// 事务拦截器
///
public class TransactionalInterceptor : BaseInterceptor
{
public TransactionalInterceptor(ILogger logger) : base(logger)
{
}
public override void InterceptHandle(IInvocation invocation)
{
if (Method.GetCustomAttribute(true) == null && Method.DeclaringType?.GetCustomAttribute(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(autofacBuilder =>
{
autofacBuilder.RegisterType();
autofacBuilder.RegisterType().As().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(autofacBuilder =>
{
autofacBuilder.RegisterType().As().SingleInstance().AsImplementedInterfaces();
autofacBuilder.RegisterType();
autofacBuilder.RegisterType().As().SingleInstance().AsImplementedInterfaces()
.EnableInterfaceInterceptors()
.InterceptedBy(typeof(UserAutoFillInterceptor));
});
用户自动拦截器:处理用户填充的
///
/// 用户自动填充拦截器
///
public class UserAutoFillInterceptor : BaseInterceptor
{
private readonly IHttpContextUser _user;
public UserAutoFillInterceptor(ILogger 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();
}
}
}
测试
__EOF__