AOP(面向切面编程)大概了解一下

前言

上一篇在聊MemoryCache的时候,用到了Autofac提供的拦截器进行面向切面编程,很明显能体会到其优点,既然涉及到了,那就趁热打铁,一块儿来探探面向切面编程。git

正文

1. 概述

在软件业,AOPAspect Oriented Programming的缩写,意为:面向切面编程,经过预编译方式和运行期间动态代理实现程序功能统一维护的一种技术。AOP是OOP(面向对象程序设计)的延续,是软件开发中的一个热点,是函数式编程的一种衍生范型。利用AOP能够对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度下降,提升程序的可重用性,同时提升了开发的效率。程序员

---来自百度百科github

总结优势数据库

  • 对业务逻辑的各个部分进行隔离,业务之间耦合度下降;
  • 提升程序的可重用性,同时程序更容易维护;
  • 提升开发效率,不用花大量的时间在业务中增长代码,还能下降风险;

其实AOP的本质就是动态代理,何为动态代理呢?编程

动态代理就是在程序运行时,建立目标对象的代理对象,并对目标对象中的方法进行功能性加强的一种技术。通俗一点来讲就是在运行期间对方法的拦截,在方法执行先后进行额外的业务处理,从而在不嵌入原有代码逻辑状况下就能加强被拦截方法的业务能力。缓存

理论先到这,一块儿来看看用代码怎么实现吧?安全

2. 实践检验真理(到底优不优秀)

先来一个控制台项目,什么都没有,从头开始撸代码,先来看看项目结构:ide

图片

老案例了,仍是伪装在进行用户维护,模拟对用户进行增删改查。此次就直接上代码啦啊,根据项目结构依次看看代码:函数式编程

  • 在AopModel中增长User.cs函数

    public class User
    {
      public string Name { get; set; }
      public int Age { get; set; }
    }
  • 在AopService中增长IUser.cs和User.cs

    IUserService.cs

    public interface IUserService
    {
      bool AddUser(User user);
    }

    UserService.cs

    public class UserService : IUserService
    {
      public bool AddUser(User user)
      {
          Console.WriteLine("用户添加成功");
          return true;
      }
    }
  • Main方法

    class Program
    {
      static void Main(string[] args)
      {
          Console.WriteLine("========原始需求=========");
          User user = new User { Name = "Zoe", Age = 18 };
          IUserService userService = new UserService();
          // 模拟增长一个用户
          userService.AddUser(user);
          Console.ReadLine();
      }
    }

这样项目就正常运行啦,这个就不用我截图了吧,小伙伴都会吧。

项目运行正常,但须要加一个需求:用户增长成功以后进行邮件发送通知。

目前有两种解决方案

  • 直接在增长用户方法中添加加发送邮件逻辑(相信不少小伙伴是这样作的,见效快,还简单);

    若是频繁在增长用户前或后添加新需求呢,还继续加吗,可能到最后增长用户的方法变得很复杂,后期也很差维护;若是要去掉某一个功能,又得把代码改回来,做为程序员是否是又要和产品同事搞架啦(文明人,不动手);固然,若是需求固定,这种方式也不错。

  • 面向切面实现,即在不影响原有代码逻辑的状况,动态的对方法进行拦截,在方法执行前或后添加业务便可。

项目中引入AOP(面向切面编程)思想
  • 原始动态代理实现;

    先来加个代理类,以下:

    using System;
    using System.Collections.Generic;
    using System.Reflection;
    using System.Text;
    namespace Aop
    {
      // 继承DispatchProxy
      public class MyProxy : DispatchProxy
      {
          //具体类型
          public object TargetClass { get; set; }
          protected override object Invoke(MethodInfo targetMethod, object[] args)
          {
              Console.WriteLine("增长用户前执行业务");
              //调用原有方法
              targetMethod.Invoke(TargetClass, args);
              Console.WriteLine("增长用户后执行业务");
              return true;
          }
      }
    }

    而后在Main函数直接使用便可,以下:

    class Program
    {
      static void Main(string[] args)
      {
          //原始需求
          User user = new User { Name = "Zoe", Age = 18 };
          IUserService userService = new UserService();
          userService.AddUser(user);
          Console.WriteLine("========动态代理 实现新需求=========");
          //1. 建立代理对象
          IUserService userService1 = DispatchProxy.Create<IUserService, MyProxy>();
          //2. 由于调用的是实例方法,须要传提具体类型
          ((MyProxy)userService1).TargetClass = new UserService();
          userService1.AddUser(user);
          Console.ReadLine();
      }
    }

    动态代理就实现需求功能啦,能够在用户增长前或后都进行相关需求处理,运行看效果:

    图片

  • 第三方库Castle.Core封装的美滋滋;

    经过上面演示,原生的动态代理实现面向切面编程显得相对麻烦,好比强制转换、传递类型等操做;经常使用的Castle.Core将动态代理进一步封装,使用就相对方便点啦;此次定义一个拦截器便可:

    using Castle.DynamicProxy;
    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace Aop
    {
      // 自定义拦截器
      public class MyIntercept : IInterceptor
      {
          public void Intercept(IInvocation invocation)
          {
              //执行原有方法以前
              Console.WriteLine("增长用户前执行业务");
              //执行原有方法
              invocation.Proceed();
              //执行原有方法以后
              Console.WriteLine("增长用户后执行业务");
          }
      }
    }

    Main函数中使用拦截器便可,以下:

    using AopModel;
    using AopService;
    using Castle.DynamicProxy;
    using System;
    using System.Reflection;
    using System.Reflection.Metadata;
    
    namespace Aop
    {
      class Program
      {
          static void Main(string[] args)
          {
              Console.WriteLine("========原始需求=========");
              User user = new User { Name = "Zoe", Age = 18 };
              IUserService userService = new UserService();
              // 模拟增长一个用户
              userService.AddUser(user);
              Console.WriteLine("========动态代理 实现新需求=========");
              //1. 建立代理对象
              IUserService userService1 = DispatchProxy.Create<IUserService, MyProxy>();
              //2. 由于调用的是实例方法,须要传提具体类型
              ((MyProxy)userService1).TargetClass = new UserService();
              userService1.AddUser(user);
              Console.WriteLine("=============Castle.Core方式==============");
              //先实例化一个代理类生成器
              ProxyGenerator generator = new ProxyGenerator();
              //经过代理类生成器建立
              var u = generator.CreateInterfaceProxyWithTarget<IUserService>(new UserService(), new MyIntercept());
              u.AddUser(user);
              Console.ReadLine();
          }
      }
    }

    运行效果以下:

    图片

  • Autofac集成了Castle.Core用着也挺不错;

    Autofac已经集成了Castle.Core啦,在聊MemoryCache的时候就已经用到,使用比较简单,能够经过特性标注的方式就能够针对某个类或接口的方法进行拦截增强,详情请参考这篇文章(因MemoryCache闹了个笑话)。

3. 应用场景

AOP思想是很优秀,但总不能到处都得用吧,需根据业务来评估是否须要;经常使用应用场景大概有如下几个:

  • 安全控制:一般在Web开发的时候,会使用过滤器或拦截器进行权限验证,这也是AOP思想的落地;对于客户端程序,经过上述演示的几种方式能够轻松实现权限的统一管理和验证;

  • 事务处理:相信小伙伴都写过数据库事务代码吧,常规作法就是在业务方法中直接开启事务,执行完成,提交或回滚便可,AOP思想也能很好处理这种状况;

  • 异常处理:统一的异常处理是最好的选择,除非是特殊的业务;一般Web有异常过滤器,客户端程序能够用上述几种方式;

  • 日志记录:目前来讲日志记录应该是做为系统功能的一部分,AOP统一记录是不错的选择;

  • 性能统计:以AOP的思想对方法进行先后监控,从而能够分析其执行性能;

  • 缓存处理:缓存处理,如上次说到MemoryCache,加上AOP拦截应用,系统效率提高不错哦

  • 业务辅变主不变:主业务变,但会不定时增长辅助需求的场景,好比增长用户,后续可能在用户新增成功以后会增长邮件通知、推送新用户信息等功能。

源码地址:https://github.com/zyq025/DotNetCoreStudyDemo

总结

先暂时聊这么多吧,瞌睡啦,小伙伴们晚安喽!!!

一个被程序搞丑的帅小伙,关注"Code综艺圈",跟我一块儿学~~~

相关文章
相关标签/搜索