前言

上一篇在聊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. Miller_Rabbin算法判断大素数

    普通的素数测试我们有O(√ n)的试除算法.事实上,我们有O(s*log³n)的算法. 下面就介绍一下Miller_Rabbin算法思想: 定理一:假如p是质数,且(a,p)=1,那么a^(p-1)≡ ...

  2. 国内centos/windows10安装minikube

    centos/windows10安装minikube 目录 centos/windows10安装minikube A win10安装minikube 1 下载安装kubectl.exe 1.1 准备目 ...

  3. codeforce 3C

    B. Lorry time limit per test 2 seconds memory limit per test 64 megabytes input standard input outpu ...

  4. 浅谈WEB前端规范化标准之ESlint

    规范化标准 软件开发需要多人开发,不同的开发者具有不同的编码习惯和喜好,不同的喜好增加项目的维护成本,所以需要明确统一的标准,决定 了项目的可维护性,人为的约定不可靠,所以需要专门的工具进行约束,并且 ...

  5. CF 1477A. Nezzar and Board

    传送门 思路: 从k = 2 * x - y ==> 2 * x = k + y ,可以看出x是k,y的中间值,则如果存在x1,x2,且x1 = x2 ± 1,则通过x1,x2可以得到所有整数, ...

  6. sentry can not delete release bug

    sentry can not delete release bug bug $ ./node_modules/@sentry/cli/bin/sentry-cli releases list $ ./ ...

  7. TDD & Unit testing

    TDD & Unit testing TDD jest https://github.com/facebook/jest https://facebook.github.io/jest/zh- ...

  8. 5G & 音频,视频

    5G & 音频,视频 直播,webtrtc 音频,视频 基础知识 基本概念.播放流程.封装格式.编解码.传输协议 音视频播放流程 主要流程:采集 -> 前处理 -> 编码 -> ...

  9. C++算法代码——求数列[coci2014/2015 contest #1]

    题目来自:http://218.5.5.242:9018/JudgeOnline/problem.php?id=1815 题目描述 Mirko在数学课上以一种有趣的方式操作数列,首先,他写下一个数列A ...

  10. 开源OA办公平台搭建教程:基于nginx的快速集群部署——端口分发

    主机信息 主机1:172.16.98.8(linux) 主机2:172.16.98.9(linux) 集群需求 172.16.98.8:WEB服务器,应用服务器,文件存储服务器,中心服务器 172.1 ...