前言

上一篇在聊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. CF1471-B. Strange List

    CF1471-B. Strange List 题意: 给定一个由\(n\)个数字组成的数组以及一个\(x\).现在从前往后遍历数组,若当前遍历的数字\(a[i]\)可以被\(x\)整除,那么就在数组的 ...

  2. K8s Deployment YAML 名词解释

    Deployment 简述 Deployment 为 Pod 和 ReplicaSet 提供了一个声明式定义 (declarative) 方法,用来替代以前的 ReplicationControlle ...

  3. kubernetes实战-交付dubbo服务到k8s集群(三)安装配置maven和java运行时环境的底包镜像

    maven 官方地址: 官方地址 下载maven,shdd7-200 # cd /opt/src # wget https://archive.apache.org/dist/maven/maven- ...

  4. Linux POSIX共享内存方法&ipcs &struct shmid_ds

    内容是主进程创建子进程计算斐波那契数列. 其中计算到第几项是有主进程命令行输入. 共享内存段,并且查看了一些信息. 参考操作系统概念第七版 3.10,3.11 关于LINUX C库函数 中的 fpri ...

  5. Leetcode(53)-最大子序和

    给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和. 示例: 输入: [-2,1,-3,4,-1,2,1,-5,4], 输出: 6 解释: 连续子数组 ...

  6. hdu2333-贪心,如何去后效性,背包太大怎么办,如何最大化最小值,从无序序列中发掘有序性质

    补充一下我理解的中文题意.. 你要重新组装电脑..电脑有一些部件..你的预算有b,b(1~1e9),有n个部件..每个部件有类型和名称以及价钱和质量现在你要在不超过预算b的情况下..每个类型都买一个部 ...

  7. u-boot 移植 --->2、在u-boot新增SOC和板子

    本次主要是要新增一个samsung的芯片到u-boot中,网上查阅资料发现s5pc1xx是与手上的S5PV210的友善的Tiny版子寄存器兼容的比较多,所以就准备以他为基础增加一个我的板子的支持到u- ...

  8. 翻译:《实用的Python编程》01_02_Hello_world

    目录 | 上一节 (1.1 Python) | 下一节 (1.3 数字) 1.2 第一个程序 本节讨论有关如何创建一个程序.运行解释器和调试的基础知识. 运行 Python Python 程序始终在解 ...

  9. Web 前端 UI 组件库文档自动化方案 All In One

    Web 前端 UI 组件库文档自动化方案 All In One 需求 自动化 动态 好用 markdown element-ui 中示例和说明按照一定规则写在md文件中,调用md-loader将md文 ...

  10. js 使用socket-io发送文件

    更多 前端 import { Component, OnInit, ViewChild, ElementRef } from '@angular/core'; import { MediaDevice ...