杂七杂八的砖编程思想AOP --- 面向切面编程
Fantasy-ke引言
AOP
AOP(Aspect-Oriented Programming) 编程思想是一种面向切面编程的编程范式。在日常的软件开发中,我们经常会遇到一些横切关注点(cross-cutting concerns),如日志记录、事务处理、权限控制、异常处理等。这些横切关注点可能会存在于程序的多个模块中,使得程序的不同模块之间存在较强的耦合性,从而影响了程序的可维护性和可扩展性。AOP编程思想的目的就是将这些横切关注点从程序的业务逻辑中剥离出来,并将其模块化处理,从而提高程序的可维护性和可扩展性。
切面
在AOP编程思想中,切面(aspect)是指横切关注点的抽象概念,它通常用一个类或一个模块来表示。切面通过将横切关注点的代码封装到独立的模块中,使得这些代码可以在程序的不同模块之间共享和复用。切面通过定义切点(pointcut)和通知(advice)来实现对横切关注点的处理。
切点
切点定义了哪些代码片段需要被处理,通知则定义了在切点处执行的处理逻辑。在AOP编程中,通知可以分为前置通知(before advice)、后置通知(after advice)、环绕通知(around advice)等不同类型,具体使用哪种类型的通知取决于需要实现的功能。
AOP 的优缺点
优点
- 解耦:AOP编程将横切关注点从业务逻辑中分离出来,使得程序各个模块之间的依赖关系降低,从而实现了解耦。
- 复用:AOP编程将横切关注点封装到独立的模块中,使得这些代码可以在程序的不同模块之间共享和复用。
- 可维护性:AOP编程将横切关注点的处理逻辑从业务逻辑中分离出来,使得程序的各个模块更加简单,易于维护。
- 可扩展性:AOP编程通过切面的定义和配置,可以很容易地扩展系统的功能,而不需要对原有的业务逻辑进行修改。
缺点
- 增加复杂性:AOP编程将程序的逻辑分散到多个模块中,增加了程序的复杂性,对于一些小规模的项目可能会显得过于复杂。
- 难以调试:由于横切关注点的代码可能被多个模块共享,所以在调试时可能会比较困难。
AOP编程思想增加了程序的复杂性,难以调试等。但总体来说,AOP编程思想对于大型软件系统的开发和维护是非常有用的。它可以使得程序的结构更加清晰、易于维护和扩展,同时也可以提高程序的重用性和可测试性。使用AOP编程思想可以让开发人员更加专注于业务逻辑的实现,而将横切关注点的处理交给AOP框架去处理,从而提高开发效率和代码质量。
C# 利用AutoFac实现简单AOP
接下来我们借助反射,Attribute,三方容器AutoFac,实现一个简单AOP:
1. 创建动态代理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
| 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; }
public T Create(T decorated, Action<object?[]?> beforeAction, Action<object?[]?, object> afterAction, Action<Exception> catchException) { 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); } }
|
2. 创建拦截器属性
标记AOP切点的Attribute–InterceptAttribut(拦截器属性)
1 2 3 4 5 6 7 8 9 10 11 12
|
[AttributeUsage(AttributeTargets.Class)] internal class InterceptAttribut : Attribute { public Type Type { get; set; } public InterceptAttribut(Type type) { this.Type = type; } }
|
3. 创建动态代理工厂类
它是泛型工厂,用于创建不同类型的代理类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class DynamicProxyFactory { 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; } }
|
4. 创建ServiceHelp
ServiceHelp用于获取实例,其核心就是以Autofac这个IOC容器去注册及获取服务.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| internal class ServiceHelp { private static ContainerBuilder builder = new ContainerBuilder();
public static IContainer? serviceProvider { get; set; }
public static void BuildServiceProvider() { 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); } }
|
5. 创建AOP切面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
|
interface IInterceptor { void BeforeExecuted(object?[]? args); void AfterExecuted(object?[]? args, object? result);
void CatchException(Exception ex);
}
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}"); } }
|
6. 创建测试模型
设定一个业务场景,有一个交通工具ITransportation有两个公共方法 Run() 、Eat(),以及其实现Hours类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 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("小马儿掉沟里了"); } }
|
7.测试场景
- 测试切面方法执行前和执行后
1 2 3 4 5 6 7 8 9 10
| static void Main(string[] args) { ServiceHelp.BuildServiceProvider();
var hours = DynamicProxyFactory.Create<ITransportation>();
hours.Eat("新鲜牧草");
Console.ReadLine(); }
|
输出:

从控制台输出可以看到,Horse再执行Eat方法前和方法后都被拦截,并输出了预期结果
- 测试切面方法异常处理
1 2 3 4 5 6 7 8 9 10
| static void Main(string[] args) { ServiceHelp.BuildServiceProvider();
var hours = DynamicProxyFactory.Create<ITransportation>();
hours.Run();
Console.ReadLine(); }
|
输出:

再Horse的Run方法中我们主动抛出了异常,然后从控制台输出可以看到,异常也被拦截器拦截,并做了处理输出拦截器中捕获到了异常及异常信息。
总结
总之,AOP编程思想是一种非常有用的编程范式,它可以使得程序的结构更加清晰、易于维护和扩展,同时也可以提高程序的重用性和可测试性。在日常的软件开发中,我们可以使用AOP框架来实现AOP编程思想,从而提高开发效率和代码质量。