了解一下IOC和AOP
简单学习一下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思想
了解一下IOC和AOP的更多相关文章
- Spring的IOC和AOP之深剖
今天,既然讲到了Spring 的IOC和AOP,我们就必须要知道 Spring主要是两件事: 1.开发Bean:2.配置Bean.对于Spring框架来说,它要做的,就是根据配置文件来创建bean实例 ...
- spring的IOC和AOP协同工作
看网络上的spring资料,基本都是在讲解IOC和AOP,但是二者是如何协同工作的,说的很少. 粗略调试了下BeanFactory的创建过程,发现是如图所示的大概过程.其中BeanPostProces ...
- spring - ioc和aop
1.程序中为什么会用到spring的ioc和aop 2.什么是IOC,AOP,以及使用它们的好处,即详细回答了第一个问题 3.原理 关于1: a:我们平常使用对象的时候,一般都是直接使用关键字类new ...
- spring的IOC和AOP
spring的IOC和AOP 1.解释spring的ioc? 几种注入依赖的方式?spring的优点? IOC你就认为他是一个生产和管理bean的容器就行了,原来需要在调用类中new的东西,现在都是 ...
- Castle框架中的IOC和AOP机制
反转控制(IOC)和面向切面编程(AOP)技术作为当前比较流行的技术,其优势已受到广泛关注,但是这两项新技术在实际项目上的应用研究却很落后,而且在.NET平台下实现这两项技术没有形成可以广泛套用的框架 ...
- 【转】spring - ioc和aop
[转]spring - ioc和aop 1.程序中为什么会用到spring的ioc和aop 2.什么是IOC,AOP,以及使用它们的好处,即详细回答了第一个问题 3.原理 关于1: a:我们平常使用对 ...
- 深入浅出学习Spring框架(四):IoC和AOP的应用——事务配置
在前文 深入浅出学习Spring框架(一):通过Demo阐述IoC和DI的优势所在. 深入浅出学习Spring框架(三):AOP 详解 分别介绍了Spring的核心功能——IoC和AOP,光讲知识远远 ...
- Spring入门导读——IoC和AOP
和MyBatis系列不同的是,在正式开始Spring入门时,我们先来了解两个关于Spring核心的概念,IoC(Inverse of Control)控制反转和AOP()面向切面编程. 1.IoC(I ...
- 六:Ioc和AOP使用拓展
Ioc和AOP使用拓展 一:1.构造注入 一个<constructor-arg>元素表示构造方法的一个参数,且使用时不区分顺序,index指定元素,位置从0开始,Type用来指定参数,避免 ...
- 反射应用--IOC和AOP
反射最大的价值就是用来写框架,下面贴出自己的3篇代码,模拟实现SPING框架的bean工厂,IOC,AOP.当然这里重点是在利用反射实现功能,为了图方便,我用的是Properties文件,关于XML后 ...
随机推荐
- 快捷方式 ABP——切换MySQL数据库
当基于ABP开发一段时间后,切换数据库可以使用快捷方式 方法如下: 第一步: 前提是要知道所使用的数据库的版本 1. vs 2017需要点击XXX.XXX.EntityFrameworkCore,右键 ...
- vim 从嫌弃到依赖(12)——打开及保存文件
在前几篇文章中,我们从vim各种模式的使用着手介绍了vim如何进行文本本身的编辑.也通过缓冲区列表的介绍了解到了vim是如何进行打开文件的管理.这篇我们将会着眼于文件的打开和保存的基本操作.通过这篇的 ...
- SqlSugar的Where用法
1.普通表达式查询 //id=@id var list=db.Queryable<Student>().Where(it => it.Id == id).ToList(); // ...
- Flask Paginate实现表格分页
flask_paginate 是 Flask 框架的一个分页扩展,用于处理分页相关的功能.它可以帮助你在 Flask Web 应用程序中实现分页功能,让用户可以浏览大量数据的不同部分.本篇博文重点讲述 ...
- 月薪10K码农,跳槽到40K架构师,技术学习路线图汇总
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.介绍 Hey there! Roadmap to becoming a web devel ...
- RabbitMQ高级知识(消息可靠性,死信交换机,惰性队列,MQ集群)
服务异步通信-高级篇 消息队列在使用过程中,面临着很多实际问题需要思考: 1.消息可靠性 消息从发送,到消费者接收,会经历多个过程: 其中的每一步都可能导致消息丢失,常见的丢失原因包括: 发送时丢失: ...
- Windows7 SP1 安装.NET Framework4失败
系统版本是Windows7旗舰版,已经安装了SP1补丁,但还是无法安装.NET Framework4或者4.5,提示安装失败. 这时可以安装.NET Framework4.8的开发包,我这里安装开发包 ...
- 利用 ASP.NET Core 开发单机应用
前言 现在是分布式微服务开发的时代,除了小工具和游戏之类刚需本地运行的程序已经很少见到纯单机应用.现在流行的Web应用由于物理隔离天然形成了分布式架构,核心业务由服务器运行,边缘业务由客户端运行.对于 ...
- static_cast, dynamic_cast与reinterpret_cast的区别
在C++中,static_cast, dynamic_cast和reinterpret_cast都可用于类型转换,它们在具体使用时有什么区别?此外,更为重要的是,为什么不推荐使用强制类型转换? 1. ...
- pandas教程02: 查找表中数据
在上篇教程中,我们介绍了pandas的安装.数据的导入与导出以及删除行列的操作.这次让我们一起研究下在pandas中如何根据指定的条件查找表中数据. 1. 数据准备 这次,我们使用一张学生成绩 ...