前言:
記得n年前,就聽過aop,曾掃過幾篇文章,不過看不懂,還是n年前,面試被問到aop的切面,還是不懂!
中秋之假,有點閑,在部落格園裡搜了下aop看了看,試圖看懂些許文章,可惜文章都說的太中規中矩,沒發現一篇能淺顯看的易懂的。
aop,全稱aspect oriented programming,中文名稱叫面向方面程式設計,也叫面向切面程式設計。
aop,你出來的意圖?
借用一圖:

不就為解耦,分離出權限/記錄檔/異常/事務等子產品出來?
這裡貼一段我項目中的代碼,最常見的修改密碼:
protected void page_load(object sender, eventargs e)
{
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);
問題說明:
幾乎每個頁面在pageload都有權限判斷語句;在更新成功時,都會有記錄檔記錄,非常之常見。
aop想讓我們幹什麼?就是讓我們,不要在每個頁面都這樣寫權限判斷和日志操作了。
不讓我這樣寫,那咋寫?aop你讓我咋寫呢?
aop說:
你可以獨立實作權限/記錄檔等子產品,然後使用動态攔截調用的方法,
在方法調用之前,你先調用[begin]函數實作權限判斷;
在方法調用之後,再調用[end]函數來寫日志.
那咋整呢?
這個,兩種方式
說起來有點複雜,借用别人的話說一下好了:
目前在.net下實作aop的方式分為兩大類:
一是采用動态代理技術,利用截取消息的方式,對該消息進行裝飾,以取代或修飾原有對象行為的執行;
二是采用靜态織入的方式,引入特定的文法建立“方面”,進而使得編譯器可以在編譯期間織入有關“方面”的代碼。
而靜态織入的方式實作一般是要依靠一些第三方架構提供特定的文法,例如postsharp,它的實作方式是采用 msil injection和msbuild task在編譯時置入方面的代碼,進而實作aop。
說白一點:
在一些類和方法上面标屬性,然後繼承特定接口或類,之後便可進行消息攔截,攔截後還是根據屬性來調用其它方法
有源碼呢:
看完源碼我說說:
我本來就兩行代碼,一行權限,一行記錄檔,
為了解耦,你弄了一堆鬼都看不懂的代碼,好吧,算是能正常運作,可是這咋應用上?
權限的類型我到哪傳?記錄檔的類型,操作的使用者名稱,我去哪拿去?你說,唉,越來越複雜了!
還有,大夥到處說你性能低呢,還有你解耦還有不就為了維護友善麼,可是你那一堆鬼都難看懂的代碼,要是換另一個人來維護,那不是天天要叫太陽。
我的想法很好的,讓你分離下權限/記錄檔,獨立出去統一管理,以後改代碼不用跑每個頁面改去。
至于實作,是相對複雜了點,為能了攔截每個方法的調用,在調用的前後插入其它方法,得底層一點,是以不平易近人了。
要簡單實作,你們還是找ioc好了,它簡單一些。
ioc:英文為 inversion of control,即反轉模式。又稱di:dependency injection,即依賴注入。
ioc說:
我可不像aop那些實作一樣,能動态攔截方法,周遊标志的屬性再執行相應的方法那麼強大而複雜。
其實我很簡單,就是在類裡随便挖個洞,能讓我鑽進去就行了。
來個簡單示例
一個接口:
public interface iaop
{
void begin();
void end();
}
注入到人家的構造函數裡:
public class maction
iaop _aop;
public maction(iaop aop)
_aop = aop;
public void add()
_aop.begin();
_aop.end();
看看
現在如果你調用add,是不是就要先執行begin方法,執行完内容之後,又要執行end方法了。
接下來,實作接口:
public class aopaction:iaop
#region iaop 成員
public void begin()
//其實我是很茫然的,我不知道我應該幹什麼,我被很多方法調用,可是,我咋知道是誰調用我呢?我要怎麼寫分支語句呢?啥都沒,隻好輸出begin...
console.writeline("begin......");
public void end()
//同樣我也很困惑,我是在人家操作完後調用的,我都不知你是更新/删除/插入還是其它,或者寫日志你得給我個id啊,啥都沒,我隻能輸出end....
console.writeline("end......");
#endregion
最後調用:
maction action = new maction(new aopaction());
action.add();
結果當然是:
begin......
<a href="http://cyq1162.cnblogs.com/">http://cyq1162.cnblogs.com/</a>
end......
當然了,ioc強大的東西不僅僅隻是在構造函數注入了,不過在哪注入,還是通過反射注入,都不是本節的重點,也不是現在關注的問題。
現在的問題是:
我上面的代碼要咋整改呢?說了一堆,應用不上,不白說了?
好吧,說正事了,咋改呢?
其實資料庫操作不外乎增删改查了。隻要在增删改查上做足功夫,基本上應用是沒問題了,複雜的應用就另外找大仙去吧。
我們隻要在每個insert/update/delete/select的方法之前與之後插入begin與end函數,同樣使用ioc注入,即可,方法不多吧。
分析下:
比如上面的權限,說到底内部還是調用select來決定有沒有權限;
上面的記錄檔,也就是update後的事情;
是以,具體代碼怎麼改,不同的架構實作改起來是不同了。
還有最重要的是,統一管理後,你怎麼傳參獲到分類與更新後的使用者名稱。
說白了begin和end函數,你得給點參數,不然叫人家方法寫啥呢?
難道頁面放個隐藏域,再request一下或取目前url來判斷分支與使用?是個方法,不過有點懸!!!
1:定義操作枚舉
public enum aopenum
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:預先實作
internal class aop:iaop
{
#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調用
public class maction : idisposable
private aop.iaop _aop=new aop.aop();//切入點
public void setaop(aop.iaop 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;
}
說明:
使用預先實作,可以不用判斷_aop是不是為null
不使用構造函數實作注入,是為了使用繼承來一次性使用setaop,進而避免到處使用setaop
架構的代碼基本就到此結束,那如何使用架構呢?
1:繼承iaop接口,如
接口實作統一管理處
對于權限判斷/記錄檔等操作,這裡集中操作了,當然也是在這裡調用其它獨立子產品就行了,至于界面,就不用寫權限判斷和日志記錄了。
2:界面調用
maction action = new maction(tablenames.users);
action.setaop(new myaop());//這句話怎麼隐掉請看下面的myaction的實作。
action.select();//權限操作
action.getcount("username='路過秋天' and password='http://cyq1162.cnblogs.com'", "login");//登陸操作
action.close();
3:輸出結果
begin方法:權限判斷執行:[users表查詢]
end 方法:記錄檔:登陸結果:失敗
1:對于getcount,我們在最後增加了附加資訊"login"來辨別是個登陸操作,對于select,也同樣可以附加辨別,當然定義成枚舉來辨別就更便捷與清晰了。
2:好像界面調用裡出現了setaop方法,那每次操作都要來一次?能不能自動點,省的一行算一行?答案是可以的。
4:定義新的操作類myaction,繼承自maction,如
public class myaction : cyq.data.maction
myaop aop;
public myaction(object tablename): base(tablename)
setaop();
public myaction(object tablename, string conn): base(tablename, conn)
private void setaop()
aop = new myaop();
base.setaop(aop);
public void setaopreuse()
這個時候,你界面調用就可以省略那句setaop函數,甚至:
在你不需要日志時可以使用:action.setnoaop();
接着又要重新使用日志功能:action.setaopreuse();
ok,本篇寫到這裡也算結束了。
結言:
希望本篇對aop或ioc一無所知的人,看了也能略懂那麼一點,深層點的請看博園的其它文章,免說我誤導初學者。
最後歡迎大家指導與交流,拒絕人參公雞~~。
版權聲明:本文原創發表于部落格園,作者為路過秋天,原文連結:
http://www.cnblogs.com/cyq1162/archive/2010/09/24/1833670.html