2.3 建議代碼
被稱作建的議代碼可以捆綁到代碼結合點。建議代碼可以被理解成當程序中相應的代碼結合點到達的時候激活的動作。AspectC++語言中用來指定建議代碼的元素是建議聲明。通過在定義了哪里、什么情況下激活代碼應該被激活的點切表達式后引入關鍵詞advice
來聲明。
例:建議聲明
advice execution("void login(...)") : before() {
count << "Logging in." << endl;
}
點切表達式后跟著的代碼片段:before()
決定了建議代碼應該直接在代碼結合點到達之前
激活。這里也可以用:after()
來表示在代碼結合點到達之后
,此外,:around()
表示建議代碼應該代碼代碼結合點描述的代碼來執行。在包圍
建議中,建議代碼可以顯式地在結合點處觸發程序代碼的執行,這樣建議代碼可以在結合點之前
和之后
執行。建議代碼并沒有特別的在結合點無視程序代碼的訪問能力。
在純粹的結合點點切描述符旁,我們也可以為將變量綁定到結合點的上下文信息中。因此,如同函數調用的真正參數值可以在建議代碼中得以訪問。
例:可訪問上下文信息的建議聲明
pointcut new_user(const char *name) = execution("void login(...)") && args(name);
advice new_user(name) : before(const char *name) {
cout << "User " << name << " is logging in." << endl;
}
上面的例子首先定義了一個綁定了上下文變量name
的點切new_user
。這意味著每當點切new_user
描述的結合點到達時,都提供了一個const char*
類型的值。在點切表達式中使用的點切函數args
會到達在程序中所有使用了const char*
類型參數的地方。因此args(name)
在執行時綁定了name
到login
函數第一也是唯一一個參數上。
例子中的跟隨在點切聲明后的建議聲明將建議代碼的執行綁定到new_user
描述的結合點到達的事件上。上下文變量持有到達的結合點的參數的真實值,它應該被聲明成before
,after
或者around
的一個正式參數。這個參數可以在建議代碼中像一個普通函數參數那樣來使用。
在點切函數args
旁,綁定的上下文參數可以由that
,target
和result
來體現 。同時,這些點切函數表現的如同相應上下文變量的過濾器一樣。就像這個例子中的args
,在過濾器上所有的結合點都有一個const char*
類型的參數。
2.3.1 引入
AspectC++支持的第二種類型的建議是引入。引入被特意用來擴展程序代碼和數據結構。下面的例子給兩個類分別擴展了一個屬性和一個方法。
例:引入
pointcut shapes() = "Circle" || "Polygon";
advice shapes() : slice class {
bool m_shaded;
void shaded(bool state) {
m_shaded = state;
}
};
就像正常的建議聲明一樣,引入用關鍵詞advice
引入。如果接下來的點切是名稱點切,點切描述的類和方法將引入切片聲明跟隨在詞素“:”后。引入代碼隨后可以像其他函數、屬性等一樣用于正常的程序代碼中。引入中的建議代碼將無視結合點的程序代碼,擁有完整的訪問權限。例如,一個類中的方法引用甚至能夠訪問那個類的私有成員。
切片也可以用來引入新的基類。接下來的例子確保所有以“Object”為結尾的類都派生自類MemoryPool
。這個類可能通過覆寫new
和delete
操作符來實現自己的內存管理。繼承自MemoryPool
的類必須重新定義純虛方法release
,它是內存管理的部分實現。這在點切中的第二行實現。
例:基類引入
advice "%Object" : slice class : public MemoryPool {
virtual void release() = 0;
}
2.3.2 建議順序
如果在同一個結合點被多個建議影響,那么就可能有必要定義建議的執行順序,因為建議代碼之間可能存在依賴關系(“方向交集”)。接下來的例子將展示如何在AspectC++中定義建議代碼的優先級。
例:建議順序
advice execution("void send(...") : order("Encrypt", "Log");
如果當函數send(...)
執行時,Encrypt
和lLog
方向的建議都要運行,以上的順序聲明將定義Encrypt
建議有更高的優先級。第8節將詳細描述更具體的建議順序和優先級。