前言

对aop进行一个阶段性的总结。

正文

首先什么是aop呢?

那么首先看aop的解决什么样的问题。

public class Program
{
public static void Main(string[] args)
{ } public void ChangePosition1()
{
// your operation
SavePosition();
} public void ChangePosition2()
{
// your operation
SavePosition();
} public void SavePosition()
{
}
}

看上面这个位置:

ChangePosition1 和 ChangePosition2

他们做完一些操作之后,需要做相同的操作,如上面所述,需要保存位置。

只有两个方法调用,那么不是啥子问题。

但是如果有大量的方法去调用这个东西,那么问题就出现了。

第一:重复性的工作

第二:出错率,每次都要写一遍,可能出现忘记的情况

第三:看着不优雅

那么在如此多的案例的情况下,人们都发现了规律。

比如说,在某个行为前做什么,在某个行为后做什么。

那么我们可以进行扩展:

public void Dosomething()
{
// your operaion
} aspect logging
{
befor(dosomething is called)
{
Log.Write("enter dosomething")
} after(dosomething is called)
{
Log.Write("after dosomething")
}
} aspect verification()
{
befor(dosomething is called)
{
// your verification
}
}

比如我们的验证和日志,可以通过这些aop去处理。

aop 全称是aspect-oriented programming 中文大多翻译过来是面向切面编程。

oriented 是面向,比如日志、验证,这些是aspect。

所以取了asepct-oriented programming 这个名字。

好的,那么现在了解了aop是什么东西,也了解了他的由来。

下面了解一下aop滥用的情况。

什么情况aop会滥用呢?

比如说:

public void setsomething()
{
} aspect shutup
{
after(something is called)
{
// shutup
}
}

这种aop 就是反模式,不被推荐的。

原因是,比如我执行了setsomething之后,我setsomething 的意思是设置某一些东西,而没有shutup的含义。

但是最后却运行了该操作,这样难以维护和理解。

那么为什么logging 和 verification 能够被人所接受呢?

因为其没有破坏我们该代码的逻辑,的确setsomething做的事情就是它应该做的事情,不对执行逻辑造成影响。

深入一下aop是如何实现的呢?其实aop的原理很简单,但是优雅实现的却有一点点复杂。

  • 字节码操作

    • 优点:字节码操作可以在更细粒度的层面上操作代码,可以实现更灵活和精细的AOP功能。可以在编译期或运行期动态修改字节码,对目标类进行修改。
    • 缺点:实现相对复杂,需要对字节码结构有一定的了解。可能会影响代码的可读性和维护性。
  • 代理技术

    • 优点:代理技术相对简单易懂,可以快速实现AOP功能。可以通过代理对象来实现横切关注点的功能,不需要直接操作字节码。
    • 缺点:代理技术通常在运行时动态创建代理对象,可能会引入性能开销。对于一些高性能要求的场景,可能不太适合。

下面对这个分别进行举例:

先从好理解的代理开始:

public class LoggingAspect
{
public void LogBefore(string methodName)
{
Console.WriteLine($"Logging before {methodName} execution");
}
}

然后创建相应的代理:

public class UserServiceProxy<T> : DispatchProxy
{
private T _decorated; private LoggingAspect _loggingAspect; protected override object? Invoke(MethodInfo? targetMethod, object?[]? args)
{
_loggingAspect.LogBefore(targetMethod.Name); return targetMethod.Invoke(_decorated, args);
} public static T Create(T decorated)
{
object proxy = Create<T, UserServiceProxy<T>>();
((UserServiceProxy<T>)proxy)._decorated = decorated;
((UserServiceProxy<T>)proxy)._loggingAspect = new LoggingAspect(); return (T)proxy;
}
}

解释一下,这个create创建了什么,这个create 创建了两个类型的继承类。

也就是创建了动态类型。

public class UserService : IUserService
{
public void Login()
{
Console.WriteLine("User logged in");
}
}

现在是我们的代码实现了。

那么看下是怎么调用的。

public static void Main(string[] args)
{
IUserService userService = new UserService();
IUserService proxy = UserServiceProxy<IUserService>.Create(userService);
proxy.Login();
}

这样就调用完成了。

看下效果。

这样就完成了相应的code。

至于为什么我们在使用框架的时候进行属性标记即可,那是因为框架帮我们把事情做了。

例如:

  1. 安装PostSharp:首先需要安装PostSharp NuGet包。

  2. 定义切面类

[Serializable]
public class LoggingAspect : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionArgs args)
{
Console.WriteLine($"Logging before {args.Method.Name} execution");
}
}
  1. 应用切面:在需要应用AOP的方法上添加切面标记。
public class UserService
{
[LoggingAspect]
public void Login()
{
Console.WriteLine("User logged in");
}
}

至于这个框架是怎么实现的,原理就是利用MSBuilder。

MSBuild工具可以通过自定义任务(Custom Tasks)来实现预处理操作。通过编写自定义任务,可以在MSBuild构建过程中执行特定的预处理逻辑。以下是一个简单的示例,演示如何在MSBuild中使用自定义任务进行预处理:

  1. 创建自定义任务
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities; public class CustomPreprocessTask : Task
{
public override bool Execute()
{
// 在这里编写预处理逻辑
Log.LogMessage("Custom preprocessing task executed.");
return true;
}
}
  1. 在项目文件中引用自定义任务

在项目文件(.csproj)中添加以下内容,引用自定义任务:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask TaskName="CustomPreprocessTask" AssemblyFile="Path\\To\\CustomTask.dll" /> <Target Name="CustomPreprocessTarget" BeforeTargets="Build">
<CustomPreprocessTask />
</Target>
</Project>
  1. 执行预处理操作

在构建项目时,MSBuild会执行自定义任务中定义的预处理逻辑。可以在Execute方法中编写任何需要的预处理代码,例如生成文件、修改配置等操作。

通过编写自定义任务并在项目文件中引用,可以利用MSBuild进行预处理操作。这样可以在构建过程中执行特定的逻辑,实现更灵活的构建流程。

代理模式,就是利用了msbuilder 预处理逻辑,在编译前进行了预处理。

那么字节码模式就是在msbuilder 编译后进行预处理。

using Mono.Cecil;
using Mono.Cecil.Cil; public class LoggingAspect
{
public void LogBefore()
{
Console.WriteLine("Logging before method execution");
}
} public class Program
{
public static void Main()
{
AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly("YourAssembly.dll");
ModuleDefinition module = assembly.MainModule; TypeDefinition type = module.Types.Single(t => t.Name == "UserService");
MethodDefinition method = type.Methods.Single(m => m.Name == "Login"); ILProcessor processor = method.Body.GetILProcessor();
Instruction firstInstruction = method.Body.Instructions.First(); // 在方法开头插入日志记录代码
processor.InsertBefore(firstInstruction, processor.Create(OpCodes.Call, typeof(LoggingAspect).GetMethod("LogBefore"))); assembly.Write("YourModifiedAssembly.dll");
}
}

就是在我们程序编译完成后,进行在相应的位置进行注入程序。

下一节oop,关于aop解决一些实际问题的例子后续补齐。

aop 阶段性概况的更多相关文章

  1. Spring AOP学习笔记01:AOP概述

    1. AOP概述 软件开发一直在寻求更加高效.更易维护甚至更易扩展的方式.为了提高开发效率,我们对开发使用的语言进行抽象,走过了从汇编时代到现在各种高级语言繁盛之时期:为了便于维护和扩展,我们对某些相 ...

  2. Spring核心框架 - AOP的原理及源码解析

    一.AOP的体系结构 如下图所示:(引自AOP联盟) 层次3语言和开发环境:基础是指待增加对象或者目标对象:切面通常包括对于基础的增加应用:配置是指AOP体系中提供的配置环境或者编织配置,通过该配置A ...

  3. Spring:AOP面向切面编程

    AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果. AOP是软件开发思想阶段性的产物,我们比较熟悉面向过程O ...

  4. AOP在大规模软件开发项目中的应用(图)

    AOP在大规模软件开发项目中的应用(图) 本文的写作源于一个真实的大型软件开发项目,我们努力尝试在这个项目中推广应用AOP.在此我们将对曾经面临过的一些实际问题与困难进行分析,试图引发关于面向方面软件 ...

  5. Spring学习一----------Spring概况

    © 版权声明:本文为博主原创文章,转载请注明出处 Spring概况 Spring是为了解决企业应用开发的复杂性而创建的. Spring是一种轻量级的控制反转(IOC)和面向切面(AOP)的容器框架. ...

  6. 老生常谈系列之Aop--Spring Aop原理浅析

    老生常谈系列之Aop--Spring Aop原理浅析 概述 上一篇介绍了AspectJ的编译时织入(Complier Time Weaver),其实AspectJ也支持Load Time Weaver ...

  7. 基于spring注解AOP的异常处理

    一.前言 项目刚刚开发的时候,并没有做好充足的准备.开发到一定程度的时候才会想到还有一些问题没有解决.就比如今天我要说的一个问题:异常的处理.写程序的时候一般都会通过try...catch...fin ...

  8. Spring基于AOP的事务管理

                                  Spring基于AOP的事务管理 事务 事务是一系列动作,这一系列动作综合在一起组成一个完整的工作单元,如果有任何一个动作执行失败,那么事务 ...

  9. 学习AOP之透过Spring的Ioc理解Advisor

    花了几天时间来学习Spring,突然明白一个问题,就是看书不能让人理解Spring,一方面要结合使用场景,另一方面要阅读源代码,这种方式理解起来事半功倍.那看书有什么用呢?主要还是扩展视野,毕竟书是别 ...

  10. 学习AOP之深入一点Spring Aop

    上一篇<学习AOP之认识一下SpringAOP>中大体的了解了代理.动态代理及SpringAop的知识.因为写的篇幅长了点所以还是再写一篇吧.接下来开始深入一点Spring aop的一些实 ...

随机推荐

  1. python 读取txt并绘制波形图实例解析

    一 用python绘图有很多方法,笔者找到了一种最简单的方法,使用非常便利,这里分享一下: import numpy as np import matplotlib.pyplot as plt a = ...

  2. 从 Linux 内核角度探秘 JDK MappedByteBuffer

    本文涉及到的内核源码版本为: 5.4 ,JVM 源码为:OpenJDK17,RocketMQ 源码版本为:5.1.1 在之前的文章<一步一图带你深入剖析 JDK NIO ByteBuffer 在 ...

  3. Vue中的$nextTick有什么作用?

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 一.NextTick是什么 官方对其的定义 在下次 DOM 更新循环结束之后执行延迟回调.在修改数据之后立即使用这个方法,获取更新后的 D ...

  4. TP6框架--EasyAdmin学习笔记:实现数据库增删查改

    这是我写的学习EasyAdmin的第三章,这一章我给大家分享下如何进行数据库的增删查改 上一章链接:点击这里前往 上一章我们说到,我仿照官方案例,定义了一条路由goodsone和创建了对应数据库,我们 ...

  5. JavaScript知识总结 闭包篇

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 1. 对闭包的理解 闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,创建的函数可以访问 ...

  6. 《Go程序设计语言》学习笔记之结构体

    <Go程序设计语言>学习笔记之结构体 一. 环境 Centos8.5, go1.17.5 linux/amd64 二. 概念 结构体是将零个或者多个任意类型的命名变量组合在一起的聚合数据类 ...

  7. vue初学核心基础02

    8.v-bind补充 8.1v-bind绑定类名 v-bind指令给"任意标签"的"任意属性"绑定数据 对于大部分的属性而言我们只需要直接赋值即可, 例如:va ...

  8. html+css实现指针时钟

    周末时间,突然想用html+css实现一个简单的指针时钟的功能,以下是具体代码实现,文末附有线上链接地址. 效果图: 1.代码 1.1.clock.html <!DOCTYPE html> ...

  9. JSON格式化 动态生成表格 表格转置 行列转换 Excel导出 行求和 列求和

    不需要行求和.列求和的查看 JSON格式化 动态生成表格 表格转置 行列转换 Excel导出 接上篇,新增行求和.列求和 完整代码如下: <!DOCTYPE html PUBLIC " ...

  10. SHELL使用教程

    疑难解答 执行完shell文件后不退出 在shell文件末尾添加如下命令即可. exec /bin/bash 参考资料 为什么sh脚本运行之后自动退出,有没有让终端不自动关闭的方法. - Ubuntu ...