简单学习一下IOC和AOP
聊一聊 IOC& AOP之前,先解释几个问题:
- AOP的老大哥OOP和老老大哥POP
- 什么是IoC?
- IoC 解决了什么问题?
- 什么是 AOP?
- AOP 解决了什么问题?
- AOP 为什么叫做切面编程?
-
什么是POP,OOP?
-
POP (Procedure-Oriented Programming)即面向过程编程:
“面向过程”(Procedure Oriented)是一种以过程为中心的编程思想。这些都是以什么正在发生为主要目标进行编程,不同于面向对象的是谁在受影响。与面向对象明显的不同就是封装、继承、类。简写为POP --百度百科
说白了就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以。
-
OOP(Object Oriented Programming)即面向对象编程:
面向对象编程(OOP)是一种基于“对象”概念的编程范式,它可以包含数据和代码:字段形式的数据(通常称为属性或属性)和代码形式的过程(通常称为方法)。--维基百科
面向对象程序设计方法是尽可能模拟人类的思维方式,使得软件的开发方法与过程尽可能接近人类认识世界、解决现实问题的方法和过程,也即使得描述问题 的问题空间与问题的解决方案空间在结构上尽可能一致,把客观世界中的实体抽象为问题域中的对象。
它具有三大特性:封装,继承,多态;
-
-
什么是IoC?
-
IoC (Inversion of control )控制反转/反转控制。它是一种思想不是一个技术实现。描述的是 :软件系统开发中领域对象的创建以及管理的问题
例如:现在有类A依赖于类B
- 传统开发方式(OOP):在类 A 中手动通过 new 关键字来 new 一个 B 对象出来。
- 使用IOC思想的开发方式:不通过 new 关键字来创建对象,而是通过 IoC 容器来帮助我们实例化对象。我们需要哪个对象,直接从 IoC 容器里面取出即可。
-
所以使用IoC思想我们丧失了创建、管理对象的权力,但是省去了我们去创建,管理对象的麻烦
-
理解一下什么叫控制反转
- 控制:指的是对象创建(实例化、管理)的权力
- 反转:控制权交给外部环境(IoC 容器)
-
-
IoC 解决了什么问题?
-
IOC出现的目的就是去除两者之间的相互依赖,由三方管理相关资源
- 降低耦合度
- 易于资源管理(例如绝大部分IOC容器默认管理的就是单例,帮你实现单例
-
假如有如下场景
如果随时代变迁,人回家的交通工具由马变成了汽车,那么我们需要在所有的人类中将交通工具的属性初始化修改为汽车的实现类像这样
那么我们如果使用IOC的思想,我们将对象的控制权(创建、管理)交给 IoC 容器去管理,我们在使用的时候由容器来提供
这样如果随着时代变迁交通再升级为飞机,那我们只需要调整容器就可以,不需要再每个依赖它的地方去修改实现;
-
说到IoC 那就一定要提DI
IoC 最常见以及最合理的实现方式叫做依赖注入(Dependency Injection,简称 DI)。
DI-Dependency Injection,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。
主要的依赖注入方式
- 构造器注入
- 属性注入
- 方法注入
-
-
什么是AOP?
-
AOP就是面向切面编程
AOP-Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术,AOP是OOP的延续。
它既然是OOP的一个延续,那就从OOP开始演化。
假设有以下模型
- Horse、 Pig、 Dog,这三个类中都有 eat 和 run 两个方法。
- 通过 OOP 思想中的继承,我们可以提取出一个 Animal 的父类,然后将 eat 和 run 方法放入父类中,
Horse
、Pig
、Dog
通过继承Animal
类即可自动获得 eat() 和 run() 方法。
OOP 编程思想可以解决大部分的代码重复问题。但是有一些问题是处理不了的。比如:日志记录、性能统计、安全校验、事务管理,等等。这些辅助逻辑往往贯穿你整个核心业务,传统 OOP 很难将其封装:
public class Animal { public void Eat(object param) { Console.WriteLine("---安全校验---"); Console.WriteLine("---性能统计 Start---"); Console.WriteLine("---日志打印 Start---"); Console.WriteLine("---事务管理 Start---"); Console.WriteLine("它吃了!!!!!!!!!!!!!"); Console.WriteLine("---事务管理 End---"); Console.WriteLine("---日志打印 End---"); Console.WriteLine("---性能统计 End---"); } public void Run(object param) { Console.WriteLine("---安全校验---"); Console.WriteLine("---性能统计 Start---"); Console.WriteLine("---日志打印 Start---"); Console.WriteLine("---事务管理 Start---"); Console.WriteLine("它跑啦!!!!!!!!!!!!!"); Console.WriteLine("---事务管理 End---"); Console.WriteLine("---日志打印 End---"); Console.WriteLine("---性能统计 End---"); } } public class Pig : Animal { public new void Eat(object param) { Console.WriteLine("---安全校验---"); Console.WriteLine("---性能统计 Start---"); Console.WriteLine("---日志打印 Start---"); Console.WriteLine("---事务管理 Start---"); Console.WriteLine("它吃了麸皮!!!!!!!!!!!!!"); Console.WriteLine("---事务管理 End---"); Console.WriteLine("---日志打印 End---"); Console.WriteLine("---性能统计 End---"); } public new void Run(object param) { Console.WriteLine("---安全校验---"); Console.WriteLine("---性能统计 Start---"); Console.WriteLine("---日志打印 Start---"); Console.WriteLine("---事务管理 Start---"); Console.WriteLine("它跑出猪圈啦!!!!!!!!!!!!!"); Console.WriteLine("---事务管理 End---"); Console.WriteLine("---日志打印 End---"); Console.WriteLine("---性能统计 End---"); } }
OOP 是至上而下的编程方式,犹如一个树状图,A调用B、B调用C,或者A继承B、B继承C。这种方式对于业务逻辑来说是合适的,通过调用或继承以复用。而辅助逻辑就像一把闸刀横向贯穿所有方法
所以AOP正是为了解决这一问题而诞生的技术
结论:
AOP 不是 OOP 的对立面,它是对 OOP 的一种补充。OOP 是纵向的,AOP 是横向的,两者相结合方能构建出良好的程序结构。AOP 技术,让我们能够不修改原有代码,便能让切面逻辑在所有业务逻辑中生效
-
应用场景
- 参数校验和判空
- 权限控制
- 日志记录
- 性能统计
- 事务处理
- 异常处理
- ...
-
-
利用AutoFac实现一个简单AOP
-
创建动态代理 ,核心就是实现了DispatchProxy这个调度代理抽象类,这个代理的作用是实现切面的功能,例如以下代理实现了方法执行前,方法执行后,及异常捕获
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); } }
-
创建动态代理工厂类 ,它是泛型工厂,用于创建不同类型的代理类
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; } }
-
在工厂创建动态代理类时,需要用到两个工具
-
一个是标记AOP切点的Attribute--InterceptAttribut(拦截器属性)
/// <summary> /// 自定义拦截器特性 /// </summary> [AttributeUsage(AttributeTargets.Class)] internal class InterceptAttribut : Attribute { public Type Type { get; set; } public InterceptAttribut(Type type) { this.Type = type; } }
-
第二个就是获取实例的ServiceHelp,其核心就是以Autofac这个IOC容器去注册及获取服务.
internal class ServiceHelp { //实例化Autofac容器 private static ContainerBuilder builder = new ContainerBuilder(); public static IContainer? serviceProvider { get; set; } public static void BuildServiceProvider(IServiceCollection services) { //将collection中的服务填充到Autofac builder.Populate(services); //注册InstanceModule组件 builder.RegisterModule<InstanceModule>(); //创建容器 serviceProvider = builder.Build(); } internal static T GetService<T>(Type serviceType) { return (T)serviceProvider.Resolve(serviceType); } } public class InstanceModule : Autofac.Module { protected override void Load(ContainerBuilder builder) { builder.RegisterType<InterceptAttribut>(); //builder.RegisterType<Horse>().As<ITransportation>(); builder.RegisterType<Car>().As<ITransportation>(); builder.RegisterType<ExecutAOP>(); } }
-
-
接下来就需要我们创建这个AOP切面
/// <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}"); } }
-
设定一个业务场景,有一个交通工具接口有两个公共方法 Run() 、Eat(),以及其两个实现, Hours 和Car
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("马儿马儿快马加鞭~~~~~~~~~~~~"); } } [InterceptAttribut(typeof(ExecutAOP))] class Car : ITransportation { public void Eat(string food) { Console.WriteLine($"大奔驰吃了{food}~~~~~~~~~~~~~~~"); } public void Run() { Console.WriteLine("奔驰奔驰跑的快~~~~~~~~~~~~~~~"); throw new Exception("奔驰撞了"); } }
-
效果
从上图可以看到结果,我们在业务逻辑中只是对业务类添加了 [InterceptAttribut(typeof(ExecutAOP))] 标记,就实现了切面中添加处理,实现了AOP思想
-