了解一下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后 ...
随机推荐
- 在bat中切换盘符
在bat代码中如何在不同的盘符中切换?直接输入盘符的名字,前面不要加cd,示例 cd %~dp0 d: cd D:\Python37 e: cd E:\Code\KSFramework c: cd C ...
- 【主流技术】浅析 ElasticSearch7.x 的基本结构及应用(一)
目录 前言 一.概述 1.1基本认识 1.2核心概念 对比关系型数据库 1.3倒排索引 例一: 例二: 1.4了解ELK 二.安装(基于 CentOS) 2.1安装声明 2.2 使用 Docker 安 ...
- 9.3 Windows驱动开发:内核解析PE结构节表
在笔者上一篇文章<内核解析PE结构导出表>介绍了如何解析内存导出表结构,本章将继续延申实现解析PE结构的PE头,PE节表等数据,总体而言内核中解析PE结构与应用层没什么不同,在上一篇文章中 ...
- Java商城单体和微服务架构有什么区别
微服务架构 概述 BizSpring移动全端国际化电商平台,是建立在Spring Cloud 基础上的微服务应用,服务化是系统达到一定规模以后的必然选择,主流的互联网公司基本都在迁移到服务化架构. 我 ...
- Hive压缩和存储
1.压缩 (1)Hive支持的压缩编码 压缩格式 工具 算法 文件扩展名 是否可切分 对应的编码/解码器 DEFLATE 无 DEFLATE .deflate 否 org.apache.hadoop. ...
- 程序员减少BUG的两个小妙招!
原创:陶朱公Boy(微信公众号ID:taozhugongboy),欢迎分享,转载请保留出处. 点评: 我们说衡量一个程序员水平的高低往往有很多因素,但有一个因素至关重要即代码质量. 如果程序员写的 ...
- Numpy基本使用方法
Numpy基本使用方法 第一节 创建数组 import numpy as np import random # 创建数组 a = [1, 2, 3, 4, 5] a1 = np.array(a) pr ...
- WPF常用控件 自定义样式( ScrollViewer TextBox PasswordBox Button RadioButton CheckBox ToggleButton ProgressBar TabControl Loading Waiting 饼图 渐变图标 消息通知 )
控件样式一览: ScrollViewer 继承样式,使用方法跟原生一致,就不过多阐述. TextBox,PasswordBox 继承样式,Tag属性为提示文字. RadioButton,CheckBo ...
- golang数组&切片&map
数组 数组声明 func main() { /* 语法一 */ // 数组名字 [数组长度] 数组类型 // 声明一个数组 长度为3 类型是int 会初始化为int类型的零值,默认值是[0 0 0 ] ...
- 【Unity3D】伽马校正
1 伽马相关概念 1.1 人眼对亮度变化的感知 人眼对亮度变化的感知不是线性的,如下图,人眼对亮区的亮度变化不太敏感,对暗区的亮度变化较敏感.另外,我们可以想象一下,在一个黑暗的房间里,由 1 根 ...