一、前言

  1.在项目中无处不充斥着记录日志的代码,各种try catch,实在是有点看着不爽。这不,果断要想法子偷个懒儿。

二、摘要

鄙人不才,先总结一下个人想到的可实现AOP的几种思路:

  1.通过继承特定实例,重写虚方法(C#中如virtual、override方法),动态构建一个该实例的子类,进行调用。

  2.通过实现特定实例上的接口,动态构建一个该接口的实现类,切入AOP代码,内部包裹特定实例的方法。

  3.最简单的一种方式,通过给特定实例继承MarshalByRefObject类,并且用继承RealProxy的代理类进行构造包裹。

代码比较少,有些Emit基础的童鞋应该很容易看懂,接下去直接上核心代码。

三、继承类模式

 if (!method.IsPublic || !method.IsVirtual/*非虚方法无法重写*/|| method.IsFinal /*Final方法无法重写,如interface的实现方法标记为 virtual final*/ || IsObjectMethod(method)) return;

             const MethodAttributes methodattributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual;
Type[] paramTypes = method.GetParameters().Select(ent => ent.ParameterType).ToArray();
MethodBuilder mb = _typeBuilder.DefineMethod(method.Name, methodattributes, method.ReturnType, paramTypes);
ILGenerator il = mb.GetILGenerator(); #region 初始化本地变量和返回值
//加载所有参数到本地object[],实例方法第一个参数是this,已排除
LoadArgsIntoLocalField(il, paramTypes); //如果有返回值,定义返回值变量
bool isReturnVoid = method.ReturnType == typeof(void);
LocalBuilder result = null;
if (!isReturnVoid)
result = il.DeclareLocal(method.ReturnType); //定义MethodInfo变量,下面会用到(传递到Aop的上下文中)
var methodInfo = il.DeclareLocal(typeof(MethodBase));
il.Emit(OpCodes.Call, typeof(MethodBase).GetMethod("GetCurrentMethod", Type.EmptyTypes));
il.Emit(OpCodes.Stloc, methodInfo);
#endregion #region 初始化AspectContext
Type contextType = typeof(AspectContext);
var context = il.DeclareLocal(contextType);
ConstructorInfo info = contextType.GetConstructor(Type.EmptyTypes);
il.Emit(OpCodes.Newobj, info);
il.Emit(OpCodes.Stloc, context);
#endregion #region 给AspectContext的参数值属性ParameterArgs,MethodInfo赋值
il.Emit(OpCodes.Ldloc, context);
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Call, contextType.GetMethod("set_ParameterArgs")); il.Emit(OpCodes.Ldloc, context);
il.Emit(OpCodes.Ldloc, methodInfo);
il.Emit(OpCodes.Call, contextType.GetMethod("set_MethodInfo"));
#endregion AspectAttribute[] attrs = GetAspectAttributes(method);
int attrLen = attrs.Length;
LocalBuilder[] lbs = new LocalBuilder[attrLen];
MethodInfo[] endInvokeMethods = new MethodInfo[attrLen];
MethodInfo[] invokingExceptionMethods = new MethodInfo[attrLen]; #region 初始化标记的切面对象,并调用切面对象的BeforeInvoke方法
for (int i = ; i < attrLen; i++)
{
var tmpAttrType = attrs[i].GetType();
var tmpAttr = il.DeclareLocal(tmpAttrType);
ConstructorInfo tmpAttrCtor = tmpAttrType.GetConstructor(Type.EmptyTypes); il.Emit(OpCodes.Newobj, tmpAttrCtor);
il.Emit(OpCodes.Stloc, tmpAttr); var beforeInvokeMethod = tmpAttrType.GetMethod("BeforeInvoke");
endInvokeMethods[i] = tmpAttrType.GetMethod("AfterInvoke");
invokingExceptionMethods[i] = tmpAttrType.GetMethod("InvokingException"); il.Emit(OpCodes.Ldloc, tmpAttr);
il.Emit(OpCodes.Ldloc, context);
il.Emit(OpCodes.Callvirt, beforeInvokeMethod);
il.Emit(OpCodes.Nop); lbs[i] = tmpAttr;
}
#endregion //try
il.BeginExceptionBlock(); #region 调用实现方法
if (!method.IsAbstract)
{
//类对象,参数值依次入栈
for (int i = ; i <= paramTypes.Length; i++)
il.Emit(OpCodes.Ldarg, i); //调用基类的方法
il.Emit(OpCodes.Call, method); if (!isReturnVoid)
{
il.Emit(OpCodes.Stloc, result); //
il.Emit(OpCodes.Ldloc, context);
il.Emit(OpCodes.Ldloc, result);
if (method.ReturnType.IsValueType)
il.Emit(OpCodes.Box, method.ReturnType);
il.Emit(OpCodes.Call, contextType.GetMethod("set_ReturnObj"));
}
}
#endregion //catch
il.BeginCatchBlock(typeof(Exception));
var exception = il.DeclareLocal(typeof(Exception));
il.Emit(OpCodes.Stloc, exception); #region 初始化ExceptionContext
var exceptionContentType = typeof(ExceptionContext);
var exceptionContent = il.DeclareLocal(exceptionContentType);
var exceptionContentCtor = exceptionContentType.GetConstructor(Type.EmptyTypes);
il.Emit(OpCodes.Newobj, exceptionContentCtor);
il.Emit(OpCodes.Stloc, exceptionContent);
#endregion #region 给ExceptionContext的参数值属性ParameterArgs,MethodInfo,ExceptionInfo,赋值
il.Emit(OpCodes.Ldloc, exceptionContent);
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Call, exceptionContentType.GetMethod("set_ParameterArgs")); il.Emit(OpCodes.Ldloc, exceptionContent);
il.Emit(OpCodes.Ldloc, methodInfo);
il.Emit(OpCodes.Call, exceptionContentType.GetMethod("set_MethodInfo")); il.Emit(OpCodes.Ldloc, exceptionContent);
il.Emit(OpCodes.Ldloc, exception);
il.Emit(OpCodes.Call, exceptionContentType.GetMethod("set_ExceptionInfo"));
#endregion #region 调用切面对象的InvokingException方法
for (int i = ; i < attrLen; i++)
{
il.Emit(OpCodes.Ldloc, lbs[i]);
il.Emit(OpCodes.Ldloc, exceptionContent);
il.Emit(OpCodes.Callvirt, invokingExceptionMethods[i]);
il.Emit(OpCodes.Nop);
}
#endregion
//try end
il.EndExceptionBlock(); #region 调用切面对象的AfterInvoke方法
for (int i = ; i < attrLen; i++)
{
il.Emit(OpCodes.Ldloc, lbs[i]);
il.Emit(OpCodes.Ldloc, context);
il.Emit(OpCodes.Callvirt, endInvokeMethods[i]);
il.Emit(OpCodes.Nop);
}
#endregion if (!isReturnVoid)
il.Emit(OpCodes.Ldloc, result); //返回
il.Emit(OpCodes.Ret);

继承类模式

该种方式,建立在使用base.XXXMethod(arg1,arg2,...)模式来调用被Aop的对象的业务方法,关键点是使用BeginExceptionBlock(),BeginCatchBlock(typeof(Exception)),EndExceptionBlock();来动态创建try catch代码块,进行异常处理。

四、实现接口模式

 if (!method.IsPublic || IsObjectMethod(method))
return; string methodName = method.Name; const MethodAttributes methodattributes = MethodAttributes.Public | MethodAttributes.Virtual;
Type[] paramTypes = method.GetParameters().Select(ent => ent.ParameterType).ToArray();
MethodBuilder methodBuilder = _typeBuilder.DefineMethod(methodName, methodattributes, method.ReturnType, paramTypes.ToArray());
var il = methodBuilder.GetILGenerator(); #region 初始化本地变量和返回值
//加载所有参数到本地object[]
LoadArgsIntoLocalField(il, paramTypes); //如果有返回值,定义返回值变量
bool isReturnVoid = method.ReturnType == typeof(void);
LocalBuilder resultLocal = null;
if (!isReturnVoid)
resultLocal = il.DeclareLocal(method.ReturnType); //定义MethodInfo变量,下面会用到(传递到Aop的上下文中)
var methodInfo = il.DeclareLocal(typeof(MethodBase));
il.Emit(OpCodes.Call, typeof(MethodBase).GetMethod("GetCurrentMethod", Type.EmptyTypes));
il.Emit(OpCodes.Stloc, methodInfo);
#endregion #region 初始化AspectContext Type contextType = typeof(AspectContext);
var context = il.DeclareLocal(contextType);
il.Emit(OpCodes.Newobj, contextType.GetConstructor(Type.EmptyTypes));
il.Emit(OpCodes.Stloc, context); #endregion #region 给AspectContext的参数值属性ParameterArgs,MethodInfo赋值
il.Emit(OpCodes.Ldloc, context);
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Call, contextType.GetMethod("set_ParameterArgs")); il.Emit(OpCodes.Ldloc, context);
il.Emit(OpCodes.Ldloc, methodInfo);
il.Emit(OpCodes.Call, contextType.GetMethod("set_MethodInfo"));
#endregion AspectAttribute[] attrs = GetAspectAttributes(method);
int attrLen = attrs.Length;
LocalBuilder[] lbs = new LocalBuilder[attrLen];
MethodInfo[] endInvokeMethods = new MethodInfo[attrLen];
MethodInfo[] invokingExceptionMethods = new MethodInfo[attrLen]; #region 初始化标记的切面对象,并调用切面对象的BeforeInvoke方法
for (int i = ; i < attrLen; i++)
{
var tmpAttrType = attrs[i].GetType();
var tmpAttr = il.DeclareLocal(tmpAttrType);
ConstructorInfo tmpAttrCtor = tmpAttrType.GetConstructor(Type.EmptyTypes); il.Emit(OpCodes.Newobj, tmpAttrCtor);
il.Emit(OpCodes.Stloc, tmpAttr); var beforeInvokeMethod = tmpAttrType.GetMethod("BeforeInvoke");
endInvokeMethods[i] = tmpAttrType.GetMethod("AfterInvoke");
invokingExceptionMethods[i] = tmpAttrType.GetMethod("InvokingException"); il.Emit(OpCodes.Ldloc, tmpAttr);
il.Emit(OpCodes.Ldloc, context);
il.Emit(OpCodes.Callvirt, beforeInvokeMethod);
il.Emit(OpCodes.Nop); lbs[i] = tmpAttr;
}
#endregion il.BeginExceptionBlock(); #region 调用实现方法
if (!method.IsAbstract)
{
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, _realProxyField);
for (int i = ; i < paramTypes.Length; i++)
il.Emit(OpCodes.Ldarg, i + ); //arg_0为当前实例,故不添加到栈。 il.Emit(OpCodes.Call, method); if (!isReturnVoid)
{
il.Emit(OpCodes.Stloc, resultLocal); //
il.Emit(OpCodes.Ldloc, context);
il.Emit(OpCodes.Ldloc, resultLocal);
if (method.ReturnType.IsValueType)
il.Emit(OpCodes.Box, method.ReturnType);
il.Emit(OpCodes.Call, contextType.GetMethod("set_ReturnObj"));
}
}
#endregion //catch
il.BeginCatchBlock(typeof(Exception));
var exception = il.DeclareLocal(typeof(Exception));
il.Emit(OpCodes.Stloc, exception); #region 初始化ExceptionContext
var exceptionContentType = typeof(ExceptionContext);
var exceptionContent = il.DeclareLocal(exceptionContentType);
var exceptionContentCtor = exceptionContentType.GetConstructor(Type.EmptyTypes);
il.Emit(OpCodes.Newobj, exceptionContentCtor);
il.Emit(OpCodes.Stloc, exceptionContent);
#endregion #region 给ExceptionContext的参数值属性ParameterArgs,MethodInfo,ExceptionInfo,赋值
il.Emit(OpCodes.Ldloc, exceptionContent);
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Call, exceptionContentType.GetMethod("set_ParameterArgs")); il.Emit(OpCodes.Ldloc, exceptionContent);
il.Emit(OpCodes.Ldloc, methodInfo);
il.Emit(OpCodes.Call, exceptionContentType.GetMethod("set_MethodInfo")); il.Emit(OpCodes.Ldloc, exceptionContent);
il.Emit(OpCodes.Ldloc, exception);
il.Emit(OpCodes.Call, exceptionContentType.GetMethod("set_ExceptionInfo"));
#endregion #region 调用切面对象的InvokingException方法
for (int i = ; i < attrLen; i++)
{
il.Emit(OpCodes.Ldloc, lbs[i]);
il.Emit(OpCodes.Ldloc, exceptionContent);
il.Emit(OpCodes.Callvirt, invokingExceptionMethods[i]);
il.Emit(OpCodes.Nop);
}
#endregion il.EndExceptionBlock(); #region 调用切面对象的AfterInvoke方法
for (int i = ; i < attrLen; i++)
{
il.Emit(OpCodes.Ldloc, lbs[i]);
il.Emit(OpCodes.Ldloc, context);
il.Emit(OpCodes.Callvirt, endInvokeMethods[i]);
il.Emit(OpCodes.Nop);
}
#endregion if (!isReturnVoid)
{
il.Emit(OpCodes.Ldloc, resultLocal);
} il.Emit(OpCodes.Ret);

实现接口模式

该种方式,与继承类模式十分类似,唯一的区别是其保存了被Aop的实例到一个全局变量,通过该全局变量进行相应的业务方法调用。

五、通过MarshalByRefObject和RealProxy

 public sealed class ProxyMarshalByRefObject<TClass> : RealProxy where TClass : class
{
private readonly MarshalByRefObject _target; public ProxyMarshalByRefObject()
: base(typeof(TClass))
{
_target = (MarshalByRefObject)Activator.CreateInstance(typeof(TClass)); #if DEBUG
// Get 'ObjRef', for transmission serialization between application domains.
ObjRef myObjRef = RemotingServices.Marshal(_target);
// Get the 'URI' property of 'ObjRef' and store it.
Console.WriteLine("URI :{0}", myObjRef.URI);
#endif
} public TClass CreateProxyType()
{
return (TClass)GetTransparentProxy();
} #region Invoke
public override IMessage Invoke(IMessage msg)
{
var call = (IMethodCallMessage)msg;
var attributes = GetAspectAttributes(call.MethodBase);
var context = new AspectContext
{
MethodInfo = call.MethodBase,
ParameterArgs = call.Args
}; PreProcess(call, attributes, context); var ctor = call as IConstructionCallMessage;
if (ctor != null)
{
var defaultProxy = RemotingServices.GetRealProxy(this._target);
defaultProxy.InitializeServerObject(ctor);
var tp = (MarshalByRefObject)this.GetTransparentProxy();
return EnterpriseServicesHelper.CreateConstructionReturnMessage(ctor, tp);
} IMethodReturnMessage resultMsg = default(IMethodReturnMessage);
var methodInfo = (this._target as TClass).GetType().GetMethod(call.MethodName);
var newArray = call.Args.ToArray(); //拷贝一份参数本地副本,用于从实际方法中接收out ,ref参数的值
try
{
var resultValue = methodInfo.Invoke(_target, newArray);
context.ReturnObj = resultValue;
resultMsg = new ReturnMessage(context.ReturnObj, newArray, newArray.Length, call.LogicalCallContext, call);
}
catch (Exception ex)
{
var exceptionContext = new ExceptionContext
{
MethodInfo = context.MethodInfo,
ParameterArgs = context.ParameterArgs,
ReturnObj = context.ReturnObj,
ExceptionInfo = ex
}; ProcessException(attributes, exceptionContext); var resultValue = methodInfo.ReturnType.IsValueType ? Activator.CreateInstance(methodInfo.ReturnType) : null;
resultMsg = new ReturnMessage(resultValue, newArray, newArray.Length, call.LogicalCallContext, call);
} PostProcess(call, resultMsg, attributes, context); return resultMsg;
}
#endregion
}

MarshalByRefObject+RealProxy

该种方式,通过一个继承了RealProxy的类,来包裹一个继承了MarshalByRefObject的类,进行拦截该被Aop的实例的方法的调用。

该方式有个难点是异常的捕获,如果使用RemotingServices.ExecuteMessage(MarshalByRefObject target, IMethodCallMessage reqMsg),那么异常捕获不到,会直接抛出到外层。那么需要使用其他的方式来进行,并且要与方法调用的上下文对应。实现思路:

1.反射出指定的Method

2.进行Try catch

3.通过重新实例化ReturnMessage返回给是调用实例。

六、性能测试

CPU:i7-3770K CPU 3.50Hz

1000次调用

1万次调用

10万次调用

100万次调用

可以看到,在调用10万次之前,一直是继承模式遥遥领先,而在10万次之后,实现接口模式效率渐渐开始忧于继承模式。

七、尾声

  代码虽简单,但实现的过程和思路曲折,绝对干货,帮您的节省写代码的数量和时间。节省了您的时间,那么您也花几秒钟点一下右下角的推荐吧。:)

  最后附上源码,点此下载源码

作者:Zachary
出处:https://zacharyfan.com/archives/27.html

▶关于作者:张帆(Zachary,个人微信号:Zachary-ZF)。坚持用心打磨每一篇高质量原创。欢迎扫描右侧的二维码~。

定期发表原创内容:架构设计丨分布式系统丨产品丨运营丨一些思考。

如果你是初级程序员,想提升但不知道如何下手。又或者做程序员多年,陷入了一些瓶颈想拓宽一下视野。欢迎关注我的公众号「跨界架构师」,回复「技术」,送你一份我长期收集和整理的思维导图。

如果你是运营,面对不断变化的市场束手无策。又或者想了解主流的运营策略,以丰富自己的“仓库”。欢迎关注我的公众号「跨界架构师」,回复「运营」,送你一份我长期收集和整理的思维导图。

大家一起Aop的更多相关文章

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

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

  2. Spring基于AOP的事务管理

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

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

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

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

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

  5. 学习AOP之认识一下Spring AOP

    心碎之事 要说知道AOP这个词倒是很久很久以前了,但是直到今天我也不敢说非常的理解它,其中的各种概念即抽象又太拗口. 在几次面试中都被问及AOP,但是真的没有答上来,或者都在面上,这给面试官的感觉就是 ...

  6. .Net中的AOP系列之构建一个汽车租赁应用

    返回<.Net中的AOP>系列学习总目录 本篇目录 开始一个新项目 没有AOP的生活 变更的代价 使用AOP重构 本系列的源码本人已托管于Coding上:点击查看. 本系列的实验环境:VS ...

  7. .NET里简易实现AOP

    .NET里简易实现AOP 前言 在MVC的过滤器章节中对于过滤器的使用就是AOP的一个实现了吧,时常在工作学习中遇到AOP对于它的运用可以说是很熟练了,就是没想过如果自己来实现的话是怎么实现的,性子比 ...

  8. 在.Net中实现自己的简易AOP

    RealProxy基本代理类 RealProxy类提供代理的基本功能.这个类中有一个GetTransparentProxy方法,此方法返回当前代理实例的透明代理.这是我们AOP实现的主要依赖. 新建一 ...

  9. 使用Java原生代理实现AOP

    ### 本文由博主柒.原创,转载请注明出处 ### 完整源码下载地址 [https://github.com/MatrixSeven/JavaAOP](https://github.com/Matri ...

  10. 【开源】.Net Aop(静态织入)框架 BSF.Aop

    BSF.Aop .Net 免费开源,静态Aop织入(直接修改IL中间语言)框架,类似PostSharp(收费): 实现前后Aop切面和INotifyPropertyChanged注入方式. 开源地址: ...

随机推荐

  1. ECMAScript 5

    2009年12月,ECMAScript 5.02011年6月,ECMAscript 5.1版发布2015年6月,ECMAScript 6正式通过,成为国际标准ES6第一个版本 ES2015,发布于20 ...

  2. left join 多个表关联时,将表值置换

    /****** Script for SelectTopNRows command from SSMS ******/ SELECT B.[GOODSID] ,A.INDUSTRY_CNAME ,C. ...

  3. Node.js之NPM工具使用

    1.NPM介绍:包管理工具 (1)允许用户从NPM服务器下载别人编写的第三方包到本地石使用 (2)允许用户从NPM服务器下载并安装别人编写的命令行程序到本地使用 (3)允许用户将自己编写的包或命令行程 ...

  4. 主机信息收集工具DMitry

    主机信息收集工具DMitry   DMitry是Kali Linux内置的一款信息收集工具.它的目标主要是Web类主机.它不仅通过主动查询.端口扫描方式,还借助第三方网站和搜索引擎获取信息. 它搜集的 ...

  5. 布局包含Image和Title的UIButton

    UIButton中的titleEdgeInsets和imageEdgeInsets可以管理button中image和title的布局. 如果对其理解不够深入,用纯数字进行布局管理,经过不断的调试,还是 ...

  6. ASP.NET MVC3中Controller与View之间的数据传递总结

    一.  Controller向View传递数据 1.       使用ViewData传递数据 我们在Controller中定义如下: ViewData["Message_ViewData& ...

  7. 【oracle】union、union all、intersect、minus 的用法及区别

    一.union与union all 首先建两个view create or replace view test_view_1 as as c from dual union as c from dua ...

  8. python基础06 循环

      循环用于重复执行一些程序. for循环  for循环需要预先设定循环的次数n,然后执行隶属于for的语句. 基本构造是 for 元素 in 序列: statement 如: for a in [1 ...

  9. Ajax浅学习

    写在前面:响应事件 function addEvent(obj, type, fn) { if (obj.addEventListener) { obj.addEventListener(type, ...

  10. 懒加载lazyload

    什么是懒加载 懒加载就是当你做滚动到页面某个位置,然后再显示当前位置的图片,这样做可以减少页面请求. 懒加载:主要目的是作为服务器前端的优化,减少请求数或延迟请求数,一些图片非常多的网站中非常有用,在 ...