使用dynamic和MEF实现轻量级的AOP组件 (3)
转摘 https://www.cnblogs.com/niceWk/archive/2010/07/22/1783068.html
水到渠成
在上一篇的《偷梁换柱》中,介绍了WeavableObject的基本实现,本篇将继续进一步探讨它的更多细节。
首先我们来看一下方法拦截点(AOP术语称为joinpoint 加入点)的位置,通常的AOP对方法的拦截有3种,一种是Before method call, 它设置在进入原方法体之前,一种是After method call,它设置在方法返回之前,还有一种就是Around,它的行为很霸道,就是不执行原方法。后来基于不同的语言特性,有些平台开始出现Before return、 After return、Before throw exception等等,通过仔细的衡量,笔者决定在一个方法中设置如下拦截点,即能保证满足大部分功能需要,又能保证结构上的简单:
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
result = null;
var mi = typeof(T).GetMethods()
.FirstOrDefault(m => m.Name == binder.Name
&& binder.ReturnType.IsAssignableFrom(m.ReturnType)
&& IsMatchParameters(m.GetParameters(), args));
if (mi != null)
{
WeavingContext context = new WeavingContext(source, binder, args);
try
{
// Before method call 拦截点
//Around method call 拦截点
//调用原方法
context.ReturnValue = mi.Invoke(source, args);
}
catch (Exception ex)
{
//异常处理拦截点
ProcessException(context, ex);
}
finally
{
// After method call 拦截点
result = context.ReturnValue;
}
return true;
}
else
{
return false;
}
}
对象属性get/set的拦截类似,大家自己可以看代码,这里从略。
解决了拦截点的定义,下一步就是如何织入拦截器方法(AOP术语叫做Advice)。初看貌似可以使用事件,也就是说在不同的拦截点触发相应的事件以此触发预先注册的拦截类的事件处理器,但使用事件有一个问题无法解决,那就是如果有多个事件处理器,我们很难保证事件处理器的执行次序。为此,特意设计了一个对象链类AspectChain(从LinkedList<T>派生),为什么不是List<T>? 主要的原因是我们需要从两头沿着节点来调用链中的对象,基本的调用原则是:
Before method call : 从链头到链尾;
Around method call: 从链头到链尾;
Exception method call:从链头到链尾;
After method call: 从链尾到链头。
AspectChain采用责任链设计模式,其中有两个方法值得注意,第一个方法是HasAroundMethod,如果链中只要任何一个对象携带有对AroundMethodCall的方法,该方法返回true,否则返回false。因为Around注入的方法和被注入的原方法二者是相互排斥的,它们之中只能有一个被执行,如果有Around方法的话,那么Around方法就要替代原方法。第二个方法是ExceptionMethodCall,我们这样来处理异常,也就是如果链中有一个对象的异常处理方法返回true,就表示异常事件不再继续传递,异常处理完毕,否则一直传递到最后一个节点,如果所有的节点都返回false,那么异常将被抛出到外部对象去处理。
现在我们对如何设计Aspect的结构的思路逐渐明朗化了,那就是每个Aspect都必须携带可以被拦截点回调的方法,实际的回调是发生在AspectChain链中的。
下面就是我们设计的IAspect接口:
public interface IAspect
{
void OnBeforeMethodCall(WeavingContext context);
bool HasArroundMethod(WeavingContext context);
void OnAroundMethodCall(WeavingContext context);
void OnAfterMethodCall(WeavingContext context);
bool OnExceptionMethodCall(WeavingContext context, Exception ex);
void OnPropertyChanging(WeavingContext context);
void OnPropertyChanged(WeavingContext context);
void OnBeforeGetValue(WeavingContext context);
void OnAfterGetValue(WeavingContext context);
List<string> Targets { get; }
bool IsMatch(string target);
int Sequence { get; set; }
bool Enabled { get; set; }
}
并不像是每个Aspect对象都要实现所有接口中的方法,所以我们就很有必要为这些对象设计一个共同的抽象基类AspectBase,这样做的好处能够从DynamicAspect的设计中看出来。
接口还包含的几个属性和一个IsMatch的方法。在上一篇要处理的问题列表中的问题6, 我们解决了前一半,那么Aspect如何匹配目标对象的问题,我们需要一种方法来辨别目标对象,笔者采用正则表达式将目标对象的类型名称和正则表达式的模板进行匹配,一个Aspect可以匹配多个目标对象,也就是我们可以在Aspect对象中指定多个模板,模板保存在Targets属性中,这样我们只要遍历这个链表就可以获取匹配的信息。IsMatch的实现在AspectBase中实现,注意IsMatch在基类被定义为虚拟的,也就是说它可以被派生类重载,允许派生类实现它们自己的匹配规则。Sequence属性用来告知对象被插入到AspectChain的次序,Enabled则作为一个开关,如果该值被设置为false, 那么这个Aspect将不再参与编织。默认值是true。下面是IsMatch接口方法在AspectBase中的实现:
public virtual bool IsMatch(string target)
{
if (Targets.Count == 0)
return true; return Targets.Any(t => Regex.IsMatch(target, t));
}
如果没有模板存在在Target表中,则认为匹配所有的目标对象类型。为了方便正则表达式模板的编写,特意设计一个助手类TargetPattern,但这个类并不一定需要,它的一个主要目的就是减少一些硬编码。比如你可以用TargetPattern.All 来代替 @“\w+?"。我们解决了目标对象的匹配问题,采用同样的方法,我们可以在重载的IAspect实现方法中让具体的Aspect类来决定如何匹配目标方法。大家可以下载包里面的例子看到,在例子中使用的是硬编码,但是具体Aspect类的实现不是DynamicAspect的任务(除了一些广泛通用的Aspect之外,DynamicAspect的未来任务就是内建对通用Aspect的实现)。每个Aspect的开发者都可以自己实现对方法的匹配算法,问题是每个具体的Aspect是如何知道调用方的信息呢?答案就在作为方法参数传递的WeavingContext对象,那么就让我们看一下这个对象都有什么成员来提供必要的信息:
public class WeavingContext
{
private DynamicMetaObjectBinder binder; public WeavingContext(object target, DynamicMetaObjectBinder binder, object[] argumentValues=null, object returnValue=null)
{
this.Target = target;
this.binder = binder;
this.ArgumentValues = argumentValues;
this.ReturnValue = returnValue;
} public object[] ArgumentValues { get; set; } public object ReturnValue { get; set; } public object Target { get; private set; }
public InvokeMemberBinder InvokeMemberBinder
{
get { return binder as InvokeMemberBinder; }
} public InvokeBinder InvokeBinder
{
get { return binder as InvokeBinder; }
} public GetIndexBinder GetIndexBinder
{
get { return binder as GetIndexBinder; }
}
public SetIndexBinder SetIndexBinder
{
get { return binder as SetIndexBinder; }
}
public GetMemberBinder GetMemberBinder
{
get { return binder as GetMemberBinder; }
}
public SetMemberBinder SetMemberBinder
{
get { return binder as SetMemberBinder; }
}
WeavingContext类包含四个重要的信息,即使用构造器参数传入对象的四个对象:
Target是目标对象实例,该对象可作为Aspect计算匹配的信息来源;Binder对象,Binder对象含有调用方绑定到动态方法上的元数据信息,如被调用的方法名称,参数个数等,不同的动态方法有不同的Binder类型,为了避免在代码中写很多if elseif 这样的语句,我们将不同类型的Binder做成属性,以方便在Aspect的方法中调用。ArgumentValues顾名思义就是参数值集合,同样ReturnValue则用于保存方法的返回值(如果有的话)。
我想我们已经解决了要解决的7个问题中的6个,剩下问题7将在下一篇当我们揭示实现DynamicAspect的另一半MEF (Managed Extensibility Framework)时予以阐述。
(未完待续)
使用dynamic和MEF实现轻量级的AOP组件 (3)的更多相关文章
- 使用dynamic和MEF实现轻量级的AOP组件 ---- 系列文章
.NET 4 实践 - 使用dynamic 和MEF实现轻量级的AOP组件(1) .NET 4 实践 - 使用dynamic和MEF实现轻量级的AOP组件 (2) .NET 4 实践 - 使用 ...
- 使用dynamic和MEF实现轻量级的AOP组件 (2)
转摘 https://www.cnblogs.com/niceWk/archive/2010/07/21/1782092.html 偷梁换柱 上一篇我们初试了DynamicAspect这把小刀,如果你 ...
- 使用dynamic 和MEF实现轻量级的 AOP 组件 (1)
转载https://www.cnblogs.com/niceWk/archive/2010/07/19/1780843.html AOP魔法 今天你AOP了吗?谈到AOP,总有一种神秘的感觉,人类对于 ...
- .NET 4 实践 - 使用dynamic和MEF实现轻量级的AOP组件 (4)
转摘 https://www.cnblogs.com/niceWk/archive/2010/07/23/1783394.html 借花献佛 前面我们介绍了构成DynamicAspect绝大部分的类, ...
- C++11实现一个轻量级的AOP框架
AOP介绍 AOP(Aspect-Oriented Programming,面向方面编程),可以解决面向对象编程中的一些问题,是OOP的一种有益补充.面向对象编程中的继承是一种从上而下的关系,不适合定 ...
- C#轻量级高性能日志组件EasyLogger
一.课程介绍 本次分享课程属于<C#高级编程实战技能开发宝典课程系列>中的第六部分,阿笨后续会计划将实际项目中的一些比较实用的关于C#高级编程的技巧分享出来给大家进行学习,不断的收集.整理 ...
- 基于DispatchProxy打造自定义AOP组件
DispatchProxy是微软爸爸编写的一个代理类,基于这个,我扩展了一个AOP组件 暂时不支持依赖注入构造方法,感觉属性注入略显麻烦,暂时没打算支持 基于特性的注入流程 [AttributeUsa ...
- 使用MEF与Castle实现AOP
MEF是微软的一个ioc框架,使用非常方便,我们只需要在需要导出的类上标记[Export],在需要使用的地方[import]就可以使用了.现在我们扩展MEF,在其装配生成实例时,使用Castle Dy ...
- 基于微软企业库的AOP组件(含源码)
软件开发,离不开对日志的操作.日志可以帮助我们查找和检测问题,比较传统的日志是在方法执行前或后,手动调用日志代码保存.但自从AOP出现后,我们就可以避免这种繁琐但又必须要实现的方式.本文是在微软企业库 ...
随机推荐
- 一份从入门到精通NLP的完整指南 | NLPer
该小博主介绍 本人:笔名zenRRan,方向自然语言处理,方法主要是深度学习. 未来的目标:人工智能之自然语言处理博士. 写公众号目的:将知识变成开源,让每个渴求知识而难以入门人工智能的小白以及想进阶 ...
- 莎士比亚电路ヾ(≧▽≦*)o
偶尔记录一件有趣的事儿! 这个电路叫做 "莎士比亚电路"[1],请自行参悟,ヾ(≧▽≦)o,ヾ(≧▽≦)o,ヾ(≧▽≦*)o ヾ(≧▽≦*)o . <穿越计算机的迷雾> ...
- zookeeper java代码实现master 选举
1,master选举使用场景及结构 现在很多时候我们的服务需要7*24小时工作,假如一台机器挂了,我们希望能有其它机器顶替它继续工作.此类问题现在多采用master-salve模式,也就是常说的主从模 ...
- jsp学习笔记day1
一.jsp简介 JSP全称Java Server Pages,是一种动态网页开发技术.它使用JSP标签在HTML网页中插入Java代码.标签通常以<%开头以%>结束. JSP是一种Java ...
- 不可被忽视的操作系统( FreeRTOS )【1】
把大多数人每个星期的双休过过成了奢侈的节假日放假,把每天23点后定义为自己的自由时间,应该如何去思考这个问题 ? 双休的两天里,不!是放假的两天里,终于有较长的时间好好的学习一下一直断断续续的Free ...
- Spring Framework之事务管理
目录 问题 数据库事务 事务的定义 事务的目的 事务的特性 事务隔离级别 数据并发问题 事务隔离级别对数据并发问题的作用 快照读 Spring事务管理 事务管理接口 TransactionDefini ...
- java文件中字母出现的次数和百分比
主要是文件的读写.先在代码中导入文件.一行一行的进行数据的读入,通过“ ”空格对读入的信息进行分割,存入到数组里之后对于每一个单词的每一个字母进行区分存入相应的字母数组里.最后统计总的字母个数.应用 ...
- C语言 文件复制
有很多人会问,学会C语言能干啥?,就只能控制台敲个数学题,做个界面都没有的贪吃蛇么? 刚开始的我,也是这样想的,但慢慢深入C语言后,我才领略到C的强大,C的万能.小到游戏破解,加解密算法,大到设备驱动 ...
- 23.1 abstract抽象类案例
package day2_抽象类; /* * 基础班老湿,就业班老湿 * 共性 * 属性 姓名,年龄,性别 * 行为 讲课 */ public class AbstractTeacherTest { ...
- Array(数组)对象-->shift() 方法
1.定义和用法 shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值. 语法: array.shift() 注意: 此方法改变数组的长度! 举例: var arr = [1,2, ...