前言

上一篇在聊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 #305 (Div. 1) B. Mike and Feet

    Mike is the president of country What-The-Fatherland. There are n bears living in this country besid ...

  2. python 引用(import)文件夹下的py文件

    importlib.import_module动态导入模块: python中schedule模块的简单使用 || importlib.import_module动态导入模块 先看一下文件目录 1.同级 ...

  3. 牛客编程巅峰赛S1第11场 - 黄金&钻石 A.牛牛的01游戏 (模拟栈)

    题意:有一个\(01\)串,两个相邻的\(0\)可以变成一个\(1\),两个相邻的\(1\)可以直接消除,问操作后的字符串. 题解:数组模拟栈直接撸,上代码吧. 代码: class Solution ...

  4. CDN 概述

    占位 CDN(Content Delivery Network)内容发布网络 推荐Blog: CDN 学习笔记

  5. HDU 3065 病毒侵袭持续中(AC自动机 模板)题解

    题意:给出主串中每个模式串的个数 思路:毒瘤出题人多组数据没说给的是多组数据. 板子: struct Aho{ struct state{ int next[130]; int fail, cnt; ...

  6. GitHub Secrets All In One

    GitHub Secrets All In One https://docs.github.com/en/free-pro-team@latest/actions/reference/encrypte ...

  7. Masterboxan INC 下半年将聚焦超高净值和家族全权委托客户

    "投资是一个没有终点的奋斗.我们不能简单的预测市场,而是应对市场做出正确的反应.这需要我们不断反思.总结.提升,找到自己的投资哲学,然后用一生的时间去坚守."Masterboxan ...

  8. JVM Attach实现原理剖析

    本文转载自JVM Attach实现原理剖析 前言 本文旨在从理论上分析JVM 在 Linux 环境下 Attach 操作的前因后果,以及 JVM 为此而设计并实现的解决方案,通过本文,我希望能够讲述清 ...

  9. [Android搞机]修改build.prop解决类原生无法链接12、13信道wifi问题

    最近xda找包刷了个机,发现没法搜到12.13信道.所有未本地化的类原生都有此问题. root后打开/system/build.prop 可以用 在build.prop中加入以下几句,重启即可连接12 ...

  10. redis5.* 手动构建集群

    1.集群的概念 集群是一组相互独立的.通过高速网络互联的计算机,它们构成了一个组,并以单一系统的模式加以管理.一个客户与集群相互作用时,集群像是一个独立的服务器.集群配置是用于提高可用性和可缩放性.当 ...