前言

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

正文

1. 概述

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

---来自百度百科

总结优点

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

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

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

理论先到这,一起来看看用代码怎么实现吧?

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

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

老案例了,还是假装在进行用户维护,模拟对用户进行增删改查。这次就直接上代码啦啊,根据项目结构依次看看代码:

  • 在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综艺圈",跟我一起学~~~

AOP(面向切面编程)大概了解一下的更多相关文章

  1. Method Swizzling和AOP(面向切面编程)实践

    Method Swizzling和AOP(面向切面编程)实践 参考: http://www.cocoachina.com/ios/20150120/10959.html 上一篇介绍了 Objectiv ...

  2. AOP 面向切面编程, Attribute在项目中的应用

    一.AOP(面向切面编程)简介 在我们平时的开发中,我们一般都是面对对象编程,面向对象的特点是继承.多态和封装,我们的业务逻辑代码主要是写在这一个个的类中,但我们在实现业务的同时,难免也到多个重复的操 ...

  3. AOP面向切面编程的四种实现

     一.AOP(面向切面编程)的四种实现分别为最原始的经典AOP.代理工厂bean(ProxyFacteryBean)和默认自动代理DefaultAdvisorAutoProxyCreator以及Bea ...

  4. Javascript aop(面向切面编程)之around(环绕)

    Aop又叫面向切面编程,其中“通知”是切面的具体实现,分为before(前置通知).after(后置通知).around(环绕通知),用过spring的同学肯定对它非常熟悉,而在js中,AOP是一个被 ...

  5. [转] AOP面向切面编程

    AOP面向切面编程 AOP(Aspect-Oriented Programming,面向切面的编程),它是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术. ...

  6. C# AOP 面向切面编程之 调用拦截

    有时候我们需要在代码中对方法调用进行拦截,并修改参数和返回值,这种操作叫做AOP(面向切面编程) 不过需要注意的是,AOP的效率很慢,在需要高效率场合慎用. 以下是C#的AOP方法: 首先建立一个控制 ...

  7. 【原创】Android AOP面向切面编程AspectJ

    一.背景: 在项目开发中,对 App 客户端重构后,发现用于统计用户行为的友盟统计代码和用户行为日志记录代码分散在各业务模块中,比如在视频模块,要想实现对用户对监控点的实时预览和远程回放行为进行统计, ...

  8. 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之十 || AOP面向切面编程浅解析:简单日志记录 + 服务切面缓存

    代码已上传Github+Gitee,文末有地址 上回<从壹开始前后端分离[ .NET Core2.0 Api + Vue 2.0 + AOP + 分布式]框架之九 || 依赖注入IoC学习 + ...

  9. 论AOP面向切面编程思想

    原创: eleven 原文:https://mp.weixin.qq.com/s/8klfhCkagOxlF1R0qfZsgg [前言] AOP(Aspect-Oriented Programming ...

随机推荐

  1. Codeforces Round #540 (Div. 3) C. Palindromic Matrix (大模拟)

    题意:给你\(n\)个数,判断是否能构成一个\(n\)X\(n\)的回文矩阵,若可以,输出\(YES\)和矩阵,否则输出\(NO\). 题解:如果这个矩阵的行/列元素是偶数的话,很好办,所有出现的数一 ...

  2. Codeforces Round #643 (Div. 2) E. Restorer Distance (贪心,三分)

    题意:给你\(n\)个数,每次可以使某个数++,--,或使某个数--另一个++,分别消耗\(a,r,m\).求使所有数相同最少的消耗. 题解:因为答案不是单调的,所以不能二分,但不难发现,答案只有一个 ...

  3. MySQL5.6 与 MySQL5.7 的区别

    目录 编译安装区别 初始化的区别 其他区别 编译安装区别 # 5.7在编译安装的时候多了一个 boost 库 [root@db02 mysql-5.7.20]# yum install -y gcc ...

  4. Navicat 使用 SSH 通道

    使用 Navicat for MySQL 通过跳板机登录 Mysql 时(使用 SSH 通道) 报错如下: SSH : Expected key exchange group packet from ...

  5. Kubernets二进制安装(3)之准备签发证书环境

    1.在mfyxw50机器上分别下载如下几个文件:cfssl.cfssl-json.cfssl-certinfo cfssl下载连接地址: https://pkg.cfssl.org/R1.2/cfss ...

  6. Vmware 15.5 ubuntu 12.04.5-desktop-i386.iso insmod后死机

    就是makefile没有问题,在其他同学的相同环境下也没有问题,但是在我的虚拟机里就会死机,复制了其他同学的虚拟机过来也会死机,所以猜想是VMware的问题. 于是下载了Virtual box,然后安 ...

  7. MarkDown(文本编译器)

    MarkDown(一种高效的文本编译器) 推荐使用Typora 点击此处下载 使用方法 1. 首先创建一个文本文件xxx.txt. 2. 然后修改文件后缀为xxx.md.(可以记做玛德...) 3. ...

  8. Doris开发手记1:解决蛋疼的MySQL 8.0连接问题

    笔者作为Apache Doris的开发者,平时感觉相关Doris的文章写的很少.主要是很多时候不知道应该去记录一些怎么样的问题,感觉写的不好就会很慌张.新的一年,希望记录自己在Doris开发过程之中所 ...

  9. 微信小程序开发抖音去水印功能

    之前找了很多抖音去水印的工具全是广告,所以索性自己写了一个,提供给大家免费试用以下是微信小程序的二维码 使用教程: 1.打开微信搜索小程序:沸点软件技术服务 2.打开沸点软件技术服务小程序 3.去抖音 ...

  10. Install wx

    Ubuntu 16.04: 由于是PY交易, 实际上是安装wxPython: pip install -U \ -f https://extras.wxpython.org/wxPython4/ext ...