简单学习一下IOC和AOP

聊一聊 IOC& AOP之前,先解释几个问题:

  • AOP的老大哥OOP和老老大哥POP
  • 什么是IoC?
  • IoC 解决了什么问题?
  • 什么是 AOP?
  • AOP 解决了什么问题?
  • AOP 为什么叫做切面编程?
  1. 什么是POP,OOP?

    1. POP (Procedure-Oriented Programming)即面向过程编程:

      ​ “面向过程”(Procedure Oriented)是一种以过程为中心的编程思想。这些都是以什么正在发生为主要目标进行编程,不同于面向对象的是谁在受影响。与面向对象明显的不同就是封装继承。简写为POP --百度百科

      说白了就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以。

    2. OOP(Object Oriented Programming)即面向对象编程:

      面向对象编程OOP)是一种基于“对象”概念的编程范式,它可以包含数据和代码:字段形式的数据(通常称为属性属性)和代码形式的过程(通常称为方法)。--维基百科

      ​ 面向对象程序设计方法是尽可能模拟人类的思维方式,使得软件的开发方法与过程尽可能接近人类认识世界、解决现实问题的方法和过程,也即使得描述问题 的问题空间与问题的解决方案空间在结构上尽可能一致,把客观世界中的实体抽象为问题域中的对象。

      ​ 它具有三大特性:封装继承多态

  2. 什么是IoC?

    1. IoC (Inversion of control )控制反转/反转控制。它是一种思想不是一个技术实现。描述的是 :软件系统开发中领域对象的创建以及管理的问题

      例如:现在有类A依赖于类B

      • 传统开发方式(OOP):在类 A 中手动通过 new 关键字来 new 一个 B 对象出来。
      • 使用IOC思想的开发方式:不通过 new 关键字来创建对象,而是通过 IoC 容器来帮助我们实例化对象。我们需要哪个对象,直接从 IoC 容器里面取出即可。
    2. 所以使用IoC思想我们丧失了创建、管理对象的权力,但是省去了我们去创建,管理对象的麻烦

    3. 理解一下什么叫控制反转

      • 控制:指的是对象创建(实例化、管理)的权力
      • 反转:控制权交给外部环境(IoC 容器)

  3. IoC 解决了什么问题?

    1. IOC出现的目的就是去除两者之间的相互依赖,由三方管理相关资源

      • 降低耦合度
      • 易于资源管理(例如绝大部分IOC容器默认管理的就是单例,帮你实现单例
    2. 假如有如下场景

    如果随时代变迁,人回家的交通工具由马变成了汽车,那么我们需要在所有的人类中将交通工具的属性初始化修改为汽车的实现类像这样

    那么我们如果使用IOC的思想,我们将对象的控制权(创建、管理)交给 IoC 容器去管理,我们在使用的时候由容器来提供

    这样如果随着时代变迁交通再升级为飞机,那我们只需要调整容器就可以,不需要再每个依赖它的地方去修改实现;

    1. 说到IoC 那就一定要提DI

      ​ IoC 最常见以及最合理的实现方式叫做依赖注入(Dependency Injection,简称 DI)。

      DI-Dependency Injection,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。

      主要的依赖注入方式

      • 构造器注入
      • 属性注入
      • 方法注入
  4. 什么是AOP?

    1. AOP就是面向切面编程

      AOP-Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术,AOP是OOP的延续。

      它既然是OOP的一个延续,那就从OOP开始演化。

      假设有以下模型

      • Horse、 Pig、 Dog,这三个类中都有 eat 和 run 两个方法。
      • 通过 OOP 思想中的继承,我们可以提取出一个 Animal 的父类,然后将 eat 和 run 方法放入父类中,HorsePigDog通过继承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 技术,让我们能够不修改原有代码,便能让切面逻辑在所有业务逻辑中生效

    1. 应用场景

      • 参数校验和判空
      • 权限控制
      • 日志记录
      • 性能统计
      • 事务处理
      • 异常处理
      • ...
  5. 利用AutoFac实现一个简单AOP

    1. 创建动态代理 ,核心就是实现了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);
      } }
    2. 创建动态代理工厂类 ,它是泛型工厂,用于创建不同类型的代理类

        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;
      }
      }
    3. 在工厂创建动态代理类时,需要用到两个工具

      1. 一个是标记AOP切点的Attribute--InterceptAttribut(拦截器属性)

          /// <summary>
        /// 自定义拦截器特性
        /// </summary>
        [AttributeUsage(AttributeTargets.Class)]
        internal class InterceptAttribut : Attribute
        {
        public Type Type { get; set; }
        public InterceptAttribut(Type type)
        {
        this.Type = type;
        }
        }
      2. 第二个就是获取实例的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>();
        }
        }
    4. 接下来就需要我们创建这个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}");
      }
      }
    5. 设定一个业务场景,有一个交通工具接口有两个公共方法 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("奔驰撞了");
      }
      }
    6. 效果

      从上图可以看到结果,我们在业务逻辑中只是对业务类添加了 [InterceptAttribut(typeof(ExecutAOP))] 标记,就实现了切面中添加处理,实现了AOP思想

了解一下IOC和AOP的更多相关文章

  1. Spring的IOC和AOP之深剖

    今天,既然讲到了Spring 的IOC和AOP,我们就必须要知道 Spring主要是两件事: 1.开发Bean:2.配置Bean.对于Spring框架来说,它要做的,就是根据配置文件来创建bean实例 ...

  2. spring的IOC和AOP协同工作

    看网络上的spring资料,基本都是在讲解IOC和AOP,但是二者是如何协同工作的,说的很少. 粗略调试了下BeanFactory的创建过程,发现是如图所示的大概过程.其中BeanPostProces ...

  3. spring - ioc和aop

    1.程序中为什么会用到spring的ioc和aop 2.什么是IOC,AOP,以及使用它们的好处,即详细回答了第一个问题 3.原理 关于1: a:我们平常使用对象的时候,一般都是直接使用关键字类new ...

  4. spring的IOC和AOP

     spring的IOC和AOP 1.解释spring的ioc? 几种注入依赖的方式?spring的优点? IOC你就认为他是一个生产和管理bean的容器就行了,原来需要在调用类中new的东西,现在都是 ...

  5. Castle框架中的IOC和AOP机制

    反转控制(IOC)和面向切面编程(AOP)技术作为当前比较流行的技术,其优势已受到广泛关注,但是这两项新技术在实际项目上的应用研究却很落后,而且在.NET平台下实现这两项技术没有形成可以广泛套用的框架 ...

  6. 【转】spring - ioc和aop

    [转]spring - ioc和aop 1.程序中为什么会用到spring的ioc和aop 2.什么是IOC,AOP,以及使用它们的好处,即详细回答了第一个问题 3.原理 关于1: a:我们平常使用对 ...

  7. 深入浅出学习Spring框架(四):IoC和AOP的应用——事务配置

    在前文 深入浅出学习Spring框架(一):通过Demo阐述IoC和DI的优势所在. 深入浅出学习Spring框架(三):AOP 详解 分别介绍了Spring的核心功能——IoC和AOP,光讲知识远远 ...

  8. Spring入门导读——IoC和AOP

    和MyBatis系列不同的是,在正式开始Spring入门时,我们先来了解两个关于Spring核心的概念,IoC(Inverse of Control)控制反转和AOP()面向切面编程. 1.IoC(I ...

  9. 六:Ioc和AOP使用拓展

    Ioc和AOP使用拓展 一:1.构造注入 一个<constructor-arg>元素表示构造方法的一个参数,且使用时不区分顺序,index指定元素,位置从0开始,Type用来指定参数,避免 ...

  10. 反射应用--IOC和AOP

    反射最大的价值就是用来写框架,下面贴出自己的3篇代码,模拟实现SPING框架的bean工厂,IOC,AOP.当然这里重点是在利用反射实现功能,为了图方便,我用的是Properties文件,关于XML后 ...

随机推荐

  1. Java中YYYY-MM-dd在跨年时出现的bug

    先看一张图: Bug的产生原因: 日期格式化时候,把 yyyy-MM-dd 写成了 YYYY-MM-dd Bug分析: 当时间是2019-08-31时, public class DateTest { ...

  2. 深入浅出Java多线程(二):Java多线程类和接口

    引言 大家好,我是你们的老伙计秀才!今天带来的是[深入浅出Java多线程]系列的第二篇内容:Java多线程类和接口.大家觉得有用请点赞,喜欢请关注!秀才在此谢过大家了!!! 在现代计算机系统中,多线程 ...

  3. 驱动开发:WinDBG 配置内核双机调试

    WinDBG 是在windows平台下,强大的用户态和内核态调试工具,相比较于Visual Studio它是一个轻量级的调试工具,所谓轻量级指的是它的安装文件大小较小,但是其调试功能却比VS更为强大, ...

  4. LyScript 插件实现UPX寻找入口

    LyScript 插件可实现对压缩壳的快速脱壳操作,目前支持两种脱壳方式,一种是运用API接口自己编写脱壳过程,另一种是直接加载现有的脱壳脚本运行脱壳. 插件地址:https://github.com ...

  5. 火山引擎ByteHouse:分析型数据库如何设计列式存储

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 列式存储通过支持按列存储数据,提供高性能的数据分析和查询.作为云原生数据仓库的 ByteHouse,也采用列式存储 ...

  6. C++ GDAL提取多时相遥感影像中像素随时间变化的数值数组

      本文介绍基于C++语言GDAL库,批量读取大量栅格遥感影像文件,并生成各像元数值的时间序列数组的方法.   首先,我们来明确一下本文所需实现的需求.现在有一个文件夹,其中包含了很多不同格式的文件, ...

  7. Android Graphics 显示系统 - 如何模拟多(物理)显示屏?

    " 本着花小钱办大事,不花钱也办事的原则,为了避免花钱买设备,那如何更便捷地学习/测试Android多屏显示的内容呢?本文就给大家介绍一种模拟Android多个物理屏幕显示的方法." ...

  8. 小知识:什么叫做workaround?

    技术人当遇到具体问题,能给出的各种解决方案,有一种类型叫做workaround,翻译过来通常为"应变方法"."变通方法": 其实这种方式通常是没有找到根本的解决 ...

  9. Python-字符串format方法指定参数

    一.字符串的format方法有几种指定参数的方式:(1)按照位置传参(默认方式),传入的参数与{}一一对应(2)关键字传参,关键字(keyword)传递是根据每个参数的名字传递参数.关键字并不用遵守位 ...

  10. CF1853

    你谷的加题速度实在太慢了 被 CF 的题目薄纱 A 可以选任意次 \(i\in [1,n]\),使 \(a[1\sim i]++,a[i+1\sim n]--\).求最少操作次数使得原数列变成非从小到 ...