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节将详细描述更具体的建议顺序和优先级。