使用dynamic 和MEF实现轻量级的 AOP 组件 (1)
转载https://www.cnblogs.com/niceWk/archive/2010/07/19/1780843.html
AOP魔法
今天你AOP了吗?谈到AOP,总有一种神秘的感觉,人类对于未知的东西一般都会有这种感觉,就像魔术,一旦揭开谜底,顿时豁然开朗。如果你愿意的话,那么就和我一起踏上AOP的揭秘之旅吧!
几年来一直为.NET框架不支持AOP特性耿耿于怀,尽管有许多第三方组件和工具在.NET平台下实现了AOP,而且其中不乏珍品,但或多或少存在着这样那样的限制。直到.NET4的发放,终于让我们有了机会来自己做一个AOP。
熬了几个夜,总算“有心不负功夫人”——DynamicAspect出世了!虽然还是在测试版阶段,但功能一点都不弱,如果你想体验一把的话,可以到这里去下一个玩玩。
不过在玩之前,你最好对AOP有那么一点点的概念,如果你全然不知AOP为何物的话,可以Google或者百度一番后,再回到这里听我摆龙套不迟。
怎么样?拿到DynamicAspect了吗?那么请随我继续前行吧。在下载的源代码包中含有一个Sample, 我们就从这个Sample开始!(强烈建议你打开VS2010,按部就班的输入下面的每一行代码。)
Sample程序实现一个极其简单的ATM功能,也就是模拟一次存款和取款的过程。
首先,在VS2010中创建一个控制台应用程序(Console Application)项目,忘记说了,是C#哦!VB的朋友别跑:),将其命名为BankSample。
在项目中,添加一个新的Bank类,添加代码如下:(不想动手的话,就复制/粘贴吧 ^_^)
class Bank
{
decimal _account; public void Withdraw(decimal amount)
{
_account -= amount;
} public void Deposit(decimal amount)
{
_account += amount;
} public void ShowAccount()
{
Console.WriteLine("Your are account amount is {0: 0.00##}", _account);
} public decimal Account
{
get { return _account; }
}
}
代码相当的简单,Withdraw从银行取款,Deposit向银行存款,_account字段保存当前银行账户数额。ShowAccount方法显示当前账户信息,Account属性返回当前账户金额。
接下来,在Program的Main方法中编写如下代码:
static void Main(string[] args)
{
Bank bank = new Bank();
Console.WriteLine("========Desposit money from bank account=============");
Console.WriteLine("Please enter deposit amount : ");
decimal amount = 0;
while (true)
{
string s = Console.ReadLine();
if (decimal.TryParse(s, out amount))
{
bank.Deposit(amount);
bank.ShowAccount();
break;
}
else
{
Console.WriteLine("The amount is incorrect, please input again: ");
}
}
Console.WriteLine("========Withdraw money from bank account=============");
Console.WriteLine("Please enter withdraw amount : ");
while (true)
{
string s = Console.ReadLine();
if (decimal.TryParse(s, out amount))
{
bank.Withdraw(amount);
bank.ShowAccount();
break;
}
else
{
Console.WriteLine("The amount is incorrect, please input again: ");
}
}
Console.ReadKey();
}
代码主要分为两个部分,上半部分是存款操作,下半部分是取款操作。编译确保代码没有错误,然后运行。
让我们观察一下上面的过程,貌似少了点什么,对,需要做安全检测!也就是说至少在调用bank.Desposit()和Bank.Withdraw()方法之前,我们要求用户输入用户名和密码。如果验证通过则继续,否则抛出安全异常。要实现这一步并不难,创建一个验证类来负责处理用户的安全验证。验证方法可以插入在Main方法的代码中,也可以插入在Bank类中需要验证的方法的代码中。考虑这样的一种情况,如果需要验证的不仅仅是Bank类的方法,在一个复杂的应用中可能有许多方法也需要做验证,那么你需要花费时间在这些代码中去插入调用验证方法的代码。更恐怖的是,如果验证的规则发生变化,比如说某些类可能需要不同的验证方式(通过调用不同的验证代码),那么你需要一一的找到这些地方,然后进行修改。故事终于出现冲突了,那就让我们来解决这个冲突吧。首先添加对DynamicAspect组件的引用,同时也添加对System.ComponentModel.Composition.dll的引用,然后像平常一样创建一个新的类:AuthenticationAspect,让这个类从AspectBase派生,在类中重载OnBeforeMethodCall方法,编写代码如下所示:
public override void OnBeforeMethodCall(WeavingContext context)
{
if (context.InvokeMemberBinder.Name != "ShowAccount")
{
Console.WriteLine("Please enter user ID: ");
var userid = Console.ReadLine();
Console.WriteLine("Please enter password: ");
var password = Console.ReadLine();
if (CheckAccount(userid, password) == false)
throw new Exception("Invalid user account!");
}
}
上面代码是自解释的,所以就不再赘言了,至于WeavingContext的参数类型,只当做不存在吧(我们以后再来讨论它)。
下面是CheckAccount的方法和实现:
private bool CheckAccount(string userid, string password)
{
return userid.ToLower() == "user" && password == "p@ssw0rd";
}
出于演示的目的,我们使用硬编码,在实际的应用中可能需要连接到数据库或者从某个地方获取用户的安全信息。
为了让我们的故事能够流畅的进行,注意到在OnBeforeMethodCall方法的代码中当用户不能通过验证时,有一个异常被抛出,所以我们需要新的类来处理异常事件。在项目中新添一个类:ExceptionAspect同样让它从AspectBase派生,不过这次重载的是OnExceptionMethodCall方法,实现方法的代码如下:
public override bool OnExceptionMethodCall(WeavingContext context, Exception ex)
{
Console.WriteLine("{0}: {1}", ex.Message, ex.InnerException.Message);
return true;
}
编译程序,确保代码没有错误,然后运行程序。什么?什么都没有发生!被忽悠了哈。且慢,引用刘谦的一句话,见证奇迹的时候到了。要做的就是对代码施加一种魔法,打开Program.cs文件,对Main方法中的的开始处作如下变化:
static void Main(string[] args)
{
// Bank bank = new Bank();
dynamic bank = new Bank().AsDynamic<Bank>();
如果出现波浪线,请添加相应的using语句。再次运行程序,相信我不说你都看到了。
没有悬念了,如果你还意犹未尽的话,我们在给这个故事一个比较完美的结局,想像一下这样的场合:如果用户输入的金额为负,会发生什么?另外如果取款的时候透支了(假如该银行不支持透支)又将如何?那就是我们需要对输入的金额(也就是方法的参数进行校验)。在项目中在添加一个新的类:ValidatorAspect, 一样从AspectBase派生。重载并实现OnBeforeMethodCall方法如下所示:
public override void OnBeforeMethodCall(WeavingContext context)
{ var methodName = context.InvokeMemberBinder.Name;
if (methodName == "Withdraw" || methodName == "Deposit")
{
var amount = (decimal)context.ArgumentValues[0];
if (amount <= 0)
throw new Exception("Amount should be greater than 0"); if (methodName == "Withdraw" && context.Target is Bank)
{
var bank = (Bank)context.Target;
if (amount > bank.Account)
throw new Exception("Amount is greater than current account.");
}
}
}
由于我们已经对代码施加过魔法了,那就编译并运行来验证不同的参数的情况。
从下一篇开始,我们将详细讲述DynamicAspect的实现原理以及一些高级应用案例。
(未完待续)
使用dynamic 和MEF实现轻量级的 AOP 组件 (1)的更多相关文章
- 使用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组件 (3)
转摘 https://www.cnblogs.com/niceWk/archive/2010/07/22/1783068.html 水到渠成 在上一篇的<偷梁换柱>中,介绍了Weavabl ...
- .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出现后,我们就可以避免这种繁琐但又必须要实现的方式.本文是在微软企业库 ...
随机推荐
- C的变量类型、作用域与生命周期的总结
C的变量类型.作用域与生命周期的总结 最近在看"C Programing Language" (Kernighan, Ritchie)关于外部变量的讨论,之前在学C的时候对这些ex ...
- coding++:Spring Boot全局事务解释及使用(一)
Spring 事务的入口: TxAdviceBeanDefinitionParser 解释 <tx:advice/> 这里将解析tx的配置. @Override protected Cla ...
- jdk下httpserver源码解析
在写这篇博客之前我查了很久发现全网都没有一篇写httpserver源码解析的 所以今天就由我来为大家解析一下httpserver的源码.(这里我会去掉其中的https部分的源码,只讲http部分,对h ...
- CentOS7配置环境变量
执行命令env查看当前环境变量: [duanyongchun@192 3DUnetCNN]$ env 例如扩展环境变量为/tmp/bin: PATH=$PATH:/tmp/bin 查看特定环境变量命令 ...
- MySql 分组函数
#二.分组函数/*功能:用作统计使用,又称为聚合函数或统计函数或组函数 分类:sum 求和.avg 平均值.max 最大值 .min 最小值 .count 计算个数 特点:1.sum.avg一般用于处 ...
- Java实现tif/tiff/bmp图片转换png图片
package org.analysisitem20181016.test; import java.io.File; import java.io.FileOutputStream; import ...
- 前端之jQuery基础篇
jQuery 是一个 JavaScript 库. jQuery 极大地简化了 JavaScript 编程. jQuery 很容易学习. jQuery 安装 网页中添加 jQuery 可以通过多种方法在 ...
- E - 不爱学习的lyb HDU - 1789(贪心策略)
众所周知lyb根本不学习.但是期末到了,平时不写作业的他现在有很多作业要做. CUC的老师很严格,每个老师都会给他一个DDL(deadline). 如果lyb在DDL后交作业,老师就会扣他的分. 现在 ...
- Spring Web Flow 笔记
在Spring 中配置 Web Flow <?xml version="1.0" encoding="UTF-8"?> <beans xmln ...
- MySQl 和 Redis
MySQL MySQL 是关系型数据库,开放源码软件,主要使用持久化存储设备(像磁盘)数据存放在磁盘中,功能强大. 因为磁盘访问速度远远慢于内存,所以访问速度慢 Redis 是非关系型,高性能的key ...