AOP(Aspect-Oriented Programming) 编程思想是一种面向切面编程的编程范式。在日常的软件开发中,我们经常会遇到一些横切关注点(cross-cutting concerns),如日志记录
、事务处理
、权限控制
、异常处理
等。这些横切关注点可能会存在于程序的多个模块中,使得程序的不同模块之间存在较强的耦合性,从而影响了程序的可维护性和可扩展性。AOP编程思想的目的就是将这些横切关注点从程序的业务逻辑中剥离出来,并将其模块化处理,从而提高程序的可维护性和可扩展性。
在AOP编程思想中,切面(aspect)是指横切关注点的抽象概念,它通常用一个类或一个模块来表示。切面通过将横切关注点的代码封装到独立的模块中,使得这些代码可以在程序的不同模块之间共享和复用。切面通过定义切点(pointcut)和通知(advice)来实现对横切关注点的处理。
切点定义了哪些代码片段需要被处理,通知则定义了在切点处执行的处理逻辑。在AOP编程中,通知可以分为前置通知(before advice)、后置通知(after advice)、环绕通知(around advice)等不同类型,具体使用哪种类型的通知取决于需要实现的功能。
AOP编程思想增加了程序的复杂性,难以调试等。但总体来说,AOP编程思想对于大型软件系统的开发和维护是非常有用的。它可以使得程序的结构更加清晰、易于维护和扩展,同时也可以提高程序的重用性和可测试性。使用AOP编程思想可以让开发人员更加专注于业务逻辑的实现,而将横切关注点的处理交给AOP框架去处理,从而提高开发效率和代码质量。
接下来我们借助反射
,Attribute
,三方容器AutoFac
,实现一个简单AOP:
csharp internal class DynamicProxy<T> : DispatchProxy
{
public T? decorated { get; set; }//目标类
public Action<object?[]?>? _BeforeAction { get; set; } // 动作之前执行
public Action<object?[]?, object>? _AfterAction { get; set; } // 动作之后执行
public Action<Exception>? _CatchExceptionAction { get; set; } // 捕获异常之后执行
protected override object? Invoke(MethodInfo? targetMethod, object?[]? args)
{
Exception exception = null;
Before(args);
object result = null;
try
{
//调用实际目标对象的方法
result = targetMethod?.Invoke(decorated, args);
}
catch (Exception ex)
{
exception = ex;
}
After(args, result);
//调用完执行方法后的委托,如果有异常,抛出异常
if (exception != null)
{
CatchException(exception);
}
return result;
}
/// <summary>
/// 创建代理实例
/// </summary>
/// <param name="decorated">代理的接口类型</param>
/// <param name="beforeAction">方法执行前执行的事件</param>
/// <param name="afterAction">方法执行后执行的事件</param>
/// <param name="catchException">异常捕获后执行的事件</param>
/// <returns></returns>
public T Create(T decorated, Action<object?[]?> beforeAction, Action<object?[]?, object> afterAction, Action<Exception> catchException)
{
// 调用DispatchProxy 的Create 创建一个新的T
object proxy = Create<T, DynamicProxy<T>>();
DynamicProxy<T> proxyDecorator = (DynamicProxy<T>)proxy;
proxyDecorator.decorated = decorated;
//把自定义的方法委托给代理类
proxyDecorator._AfterAction = afterAction;
proxyDecorator._BeforeAction = beforeAction;
proxyDecorator._CatchExceptionAction = catchException;
return (T)proxy;
}
private void Before(object?[]? args)
{
try
{
_BeforeAction.Invoke(args);
}
catch (Exception ex)
{
Console.WriteLine($"执行之前异常:{ex.Message}");
}
}
private void After(object?[]? args, object? result)
{
try
{
_AfterAction.Invoke(args, result);
}
catch (Exception ex)
{
Console.WriteLine($"执行之后异常:{ex.Message}");
}
}
private void CatchException(Exception ex)
{
_CatchExceptionAction(ex);
}
}
标记AOP切点的Attribute--InterceptAttribut(拦截器属性)
csharp /// <summary>
/// 自定义拦截器特性
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
internal class InterceptAttribut : Attribute
{
public Type Type { get; set; }
public InterceptAttribut(Type type)
{
this.Type = type;
}
}
它是泛型工厂,用于创建不同类型的代理类
csharp class DynamicProxyFactory
{
/// <summary>
/// 创建代理实例
/// </summary>
/// <param name="decorated">代理的接口类型</param>
/// <returns></returns>
public static T Create<T>()
{
var decorated = ServiceHelp.GetService<T>(typeof(T));
var type = decorated.GetType();
var interceptAttribut = type.GetCustomAttribute<InterceptAttribut>();
var interceptor = ServiceHelp.GetService<IInterceptor>(interceptAttribut.Type);
//创建代理类
var proxy = new DynamicProxy<T>().Create(decorated, interceptor.BeforeExecuted, interceptor.AfterExecuted, interceptor.CatchException);
return proxy;
}
}
ServiceHelp用于获取实例,其核心就是以Autofac这个IOC容器去注册及获取服务.
csharp internal class ServiceHelp
{
//实例化Autofac容器
private static ContainerBuilder builder = new ContainerBuilder();
public static IContainer? serviceProvider { get; set; }
public static void BuildServiceProvider()
{
//注册InstanceModule组件
builder.RegisterModule<InstanceModule>();
//创建容器
serviceProvider = builder.Build();
}
internal static T GetService<T>(Type serviceType)
{
if (serviceProvider.IsRegistered(serviceType))
{
return (T)serviceProvider.Resolve(serviceType);
}
return default(T);
}
}
csharp /// <summary>
/// 自定义拦截器接口
/// </summary>
interface IInterceptor
{
/// <summary>
/// 执行前
/// </summary>
/// <param name="args"></param>
void BeforeExecuted(object?[]? args);
/// <summary>
/// 执行后
/// </summary>
/// <param name="args">参数</param>
/// <param name="result">返回值</param>
void AfterExecuted(object?[]? args, object? result);
void CatchException(Exception ex);
}
/// <summary>
/// 方法执行的切面
/// </summary>
class ExecutAOP : IInterceptor
{
public void AfterExecuted(object?[]? args, object? result)
{
Console.WriteLine($"拦截器中方法后执行~~~~");
}
public void BeforeExecuted(object?[]? args)
{
if (args != null && args.Length > 0 && args[0] == null)
throw new Exception("参数错误");
Console.WriteLine($"拦截器中方法前执行~~~~");
}
public void CatchException(Exception ex)
{
Console.WriteLine($"拦截器中捕获到了异常~~~~\r\n{ex.InnerException.Message}");
}
}
设定一个业务场景,有一个交通工具ITransportation
有两个公共方法 Run()
、Eat()
,以及其实现Hours
类
csharp interface ITransportation
{
public void Run();
public void Eat(string food);
}
[InterceptAttribut(typeof(ExecutAOP))]
class Horse : ITransportation
{
public void Eat(string food)
{
Console.WriteLine($"小马儿吃了{food}~~~~~~~~~~~~");
}
public void Run()
{
Console.WriteLine("马儿马儿快马加鞭~~~~~~~~~~~~");
throw new Exception("小马儿掉沟里了");
}
}
测试切面方法执行前和执行后
csharp static void Main(string[] args)
{
ServiceHelp.BuildServiceProvider();
var hours = DynamicProxyFactory.Create<ITransportation>();
hours.Eat("新鲜牧草");
Console.ReadLine();
}
输出:
从控制台输出可以看到,Horse
再执行Eat
方法前和方法后都被拦截,并输出了预期结果
csharp static void Main(string[] args)
{
ServiceHelp.BuildServiceProvider();
var hours = DynamicProxyFactory.Create<ITransportation>();
hours.Run();
Console.ReadLine();
}
输出:
再Horse
的Run
方法中我们主动抛出了异常,然后从控制台输出可以看到,异常也被拦截器拦截,并做了处理输出拦截器中捕获到了异常
及异常信息。
总之,AOP编程思想是一种非常有用的编程范式,它可以使得程序的结构更加清晰、易于维护和扩展,同时也可以提高程序的重用性和可测试性。在日常的软件开发中,我们可以使用AOP框架来实现AOP编程思想,从而提高开发效率和代码质量。
本文作者:Peter.Pan
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!