前言:
中秋之假,有点闲,在博客园里搜了下AOP看了看,试图看懂些许文章,可惜文章都说的太中规中矩,没发现一篇能浅显看的易懂的。
AOP,全称Aspect Oriented Programming,中文名称叫面向方面编程,也叫面向切面编程。
AOP,你出来的意图?
借用一图:
不就为解耦,分离出权限/操作日志/异常/事务等模块出来?
这里贴一段我项目中的代码,最常见的修改密码:
{
if (!Page.IsPostBack)
{
SetPermission(SysModule.修改密码).HasUpdatePermission();
lblUser.Text = CurrentUser.User_Name;
}
}
protected void btnOK_Click(object sender, EventArgs e)
{
if (CurrentUser.Password == tbxPassword.Text)
{
UserListBean entity = new UserListBean();
entity.ID = CurrentUser.ID.Value;
entity.Password = tbxPwdNew.Text;
if (Factory<UserListBean>.Instance.Update(entity))
{
LogWrite.Write(LogModule.用户管理, LogType.编辑, string.Format("修改密码[用户名称={0}]", CurrentUser.User_Name));
CommonHelper.ShowDoneMessage();
}
}
Show("旧密码错误!",Request.RawUrl);
}
问题说明:
AOP想让我们干什么?就是让我们,不要在每个页面都这样写权限判断和日志操作了。
不让我这样写,那咋写?AOP你让我咋写呢?
AOP说:
你可以独立实现权限/操作日志等模块,然后使用动态拦截调用的方法,
在方法调用之前,你先调用[Begin]函数实现权限判断;
在方法调用之后,再调用[End]函数来写日志.
那咋整呢?
AOP说:
这个,两种方式
说起来有点复杂,借用别人的话说一下好了:
说白一点:
有源码呢:
我这里有一份动代代理实现拦截的代码,可点击下载
看完源码我说说:
为了解耦,你弄了一堆鬼都看不懂的代码,好吧,算是能正常运行,可是这咋应用上?
权限的类型我到哪传?操作日志的类型,操作的用户名称,我去哪拿去?你说,唉,越来越复杂了!
还有,大伙到处说你性能低呢,还有你解耦还有不就为了维护方便么,可是你那一堆鬼都难看懂的代码,要是换另一个人来维护,那不是天天要叫太阳。
AOP说:
我的想法很好的,让你分离下权限/操作日志,独立出去统一管理,以后改代码不用跑每个页面改去。
至于实现,是相对复杂了点,为能了拦截每个方法的调用,在调用的前后插入其它方法,得底层一点,所以不平易近人了。
要简单实现,你们还是找IOC好了,它简单一些。
IOC:英文为 Inversion of Control,即反转模式。又称DI:Dependency Injection,即依赖注入。
IOC说:
我可不像AOP那些实现一样,能动态拦截方法,遍历标志的属性再执行相应的方法那么强大而复杂。
其实我很简单,就是在类里随便挖个洞,能让我钻进去就行了。
来个简单示例
一个接口:
{
void Begin();
void End();
}
注入到人家的构造函数里:
{
IAop _Aop;
public MAction(IAop aop)
{
_Aop = aop;
}
public void Add()
{
_Aop.Begin();
Console.WriteLine("http://cyq1162.cnblogs.com/");
_Aop.End();
}
}
看看
接下来,实现接口:
{
#region IAop 成员
public void Begin()
{
Console.WriteLine("begin......");
}
public void End()
{
Console.WriteLine("end......");
}
#endregion
}
最后调用:
action.Add();
结果当然是:
http://cyq1162.cnblogs.com/
当然了,IOC强大的东西不仅仅只是在构造函数注入了,不过在哪注入,还是通过反射注入,都不是本节的重点,也不是现在关注的问题。
现在的问题是:
我上面的代码要咋整改呢?说了一堆,应用不上,不白说了?
好吧,说正事了,咋改呢?
我们只要在每个Insert/Update/Delete/Select的方法之前与之后插入Begin与End函数,同样使用IOC注入,即可,方法不多吧。
分析下:
还有最重要的是,统一管理后,你怎么传参获到分类与更新后的用户名称。
举例说明使用:这里使用 CYQ.Data 框架 来说明
1:定义操作枚举
{
Select,
Insert,
Update,
Delete,
Fill,
GetCount,
ExeMDataTable,
ExeNonQuery,
ExeScalar
}
2:定义IAop接口
{
void Begin(AopEnum action,string objName, params object[] aopInfo);
void End(AopEnum action, bool success, object id, params object[] aopInfo);
void OnError(string msg);
}
3:预先实现
{
#region IAop 成员
public void Begin(AopEnum action, string objName, params object[] aopInfo){}
public void End(AopEnum action, bool success, object id, params object[] aopInfo){}
public void OnError(string msg){}
#endregion
}
4:内部用Setter方式开个洞,不使用构造函数注入,方法里增加Begin调用与End调用
{
private Aop.IAop _Aop=new Aop.Aop();//切入点
public void SetAop(Aop.IAop aop)
{
_Aop = aop;
}
public bool Fill(object where, params object[] aopInfo)
{
_Aop.Begin(Aop.AopEnum.Fill,_TableName, aopInfo);
bool result = 操作结果
_Aop.End(Aop.AopEnum.Fill, result, aopInfo);
return result;
}
}
说明:
不使用构造函数实现注入,是为了使用继承来一次性使用SetAop,从而避免到处使用SetAop
框架的代码基本就到此结束,那如何使用框架呢?
1:继承IAop接口,如
/// <summary>
/// 作者:路过秋天
/// 博客:http://cyq1162.cnblogs.com
/// </summary>
public class MyAop:CYQ.Data.Aop.IAop
{
#region IAop 成员
public void Begin(CYQ.Data.Aop.AopEnum action, string objName, params object[] aopInfo)
{
switch (action)
{
case CYQ.Data.Aop.AopEnum.Select:
HttpContext.Current.Response.Write(string.Format("Begin方法:权限判断执行:[{0}表查询]<br>",objName));
break;
}
}
public void End(CYQ.Data.Aop.AopEnum action, bool success, object id, params object[] aopInfo)
{
switch (action)
{
case CYQ.Data.Aop.AopEnum.GetCount:
if (aopInfo.Length > 0)
{
switch (aopInfo[0].ToString())
{
case "Login":
HttpContext.Current.Response.Write("End 方法:操作日志:登陆结果:" + (success ? "成功" : "失败") + "<br>");
break;
}
}
break;
}
}
public void OnError(string msg)
{
throw new Exception("The method or operation is not implemented.");
}
#endregion
}
说明:
2:界面调用
action.SetAop(new MyAop());//这句话怎么隐掉请看下面的MyAction的实现。
action.Select();//权限操作
action.GetCount("UserName='路过秋天' and Password='http://cyq1162.cnblogs.com'", "Login");//登陆操作
action.Close();
3:输出结果
End 方法:操作日志:登陆结果:失败
说明:
2:好像界面调用里出现了SetAop方法,那每次操作都要来一次?能不能自动点,省的一行算一行?答案是可以的。
4:定义新的操作类MyAction,继承自MAction,如
{
MyAop aop;
public MyAction(object tableName): base(tableName)
{
SetAop();
}
public MyAction(object tableName, string conn): base(tableName, conn)
{
SetAop();
}
private void SetAop()
{
aop = new MyAop();
base.SetAop(aop);
}
public void SetAopReuse()
{
base.SetAop(aop);
}
}
说明:
在你不需要日志时可以使用:action.SetNoAop();
接着又要重新使用日志功能:action.SetAopReuse();
OK,本篇写到这里也算结束了。
结言:
希望本篇对Aop或Ioc一无所知的人,看了也能略懂那么一点,深层点的请看博园的其它文章,免说我误导初学者。
本框架对Aop的最新应用请到 CYQ.Data 轻量数据层之路 bug反馈、优化建议、最新框架下载 下载抢先体验版本。
最后欢迎大家指导与交流,拒绝人参公鸡~~。