对软件系统来说升级就意味着风险,至少对数据和功能来说可能有不经意的破坏。尤其在电信系统中,通常要求严格的架构可用性和质量可靠边。增加一个新的功能经常充满困难的—就像其他如飞行模拟领域一样。
在这篇文章中,我会使用AOP来拦截我想要升级的方法。被拦截的代码就可以为操作系统提供过度的功能。对于下一个主版本(升级后的版本)来说,升级代码可以从被拦截的地方移到主版本代码中。在这里拦截的使用减少了升级主类的风险。
被拦截的代码是通过下面基于dynaop脚本文件来建立的(可参考dynaop project on java.net获取更多信息)。这个脚本文件注释一个类并为其在运行是被其他类拦截作准备。这并不是一个可执行脚本文件。
// Apply interceptor to all getter methods in the class NMSSubject.interceptor(
NMSSubject.class,
GET_METHODS,
new UpgradeInterceptor());
NMSSubject类是一个Observer模式中的客户端;我们相要升级getNewData()方法。UpgradeInterceptor类提供了拦截getNewData()的AOP实现。一旦拦截成功,方法可以被参数化(调用JNDI命名服务)并且扩展的数据可以透明地返回给原来的客户端。
The Takeaway 我会使用Observer模式并且通过AOP升级一个子类方法。这里也显示了处理数据生成和使用的代码分离的意义。此外还顺便介绍一些软件架构的概念。
Observer模式 Observer模式(也称为发布-订阅)对设计包含一个服务器和多个客户端的类来说是非常有用的。他鼓励使用数据生成和使用分离的概念。一个来自网络管理的例子是一个服务器应用维护一个虚拟的设备网络拓朴图。他通过拉(主动请求)和(被动)听的组合来获取与设备和链接状态相关的消息。图1显示了一个服务器与一组网络设备通讯的方式。一组客户端通过Observer模式来得到服务器的数据。
Figure 1. Topology Server and the Observer Pattern
由服务器维护的图的真实度决定了用户对被管网络的了解。许多设备类型都支持自己的搜索方式;例如IP路由会尽力维护一份最新的周围网络图。如果一个远连接失败了,路由必须尽力计算出他对内部路由表的影响。这个过程称为收敛并且通常被认为是通用搜索问题的一个特例。更多与之相关的信息可以参考《网络管理,MIB和MPLS:原则,设计和实现》
回到图1,拓朴图可以用来与客户端通讯并且使用Observer模式保持最新。换句话说,当服务器检测到网络中的变更时他会使用Observer模式中相关方法与客户端通讯。
观察者主题 我的数据生成器(发布者)一个实现Subject接口的类NMSSubject。
你可以看到,Subject有三个方法允许加入/删除/通知观察者(通知是用来更新观察者的数据)。这些方法对NMSSubject的集合对象进行操作(NMS表示网络管理系统)。
public class NMSSubject implements Subject
{
Collection observers = new ArrayList();
public void addObserver(Observer observer)
{
observers.add(observer);
}
public void removeObserver(Observer observer)
{
this.observers.remove(observer);
}
public void notifyObservers()
{
for (Iterator i = observers.iterator();
i.hasNext();)
((Observer) i.next()).update(this);
}
// Have AOP intercept the next call and in turn call into JNDI
// The legacy code might have been reading from a file or database.
// The intercepted code could call into JNDI or some other API
// This can be used as a temporary measure to avoid an upgrade
public String getNewData()
{
return "Data Set: ";
// Call into JNDI here via AOP }}
NMSSubject中的最后一个方法是getNewData(),也是我们需要升级的代码。我们想通过AOP拦截getNewData()并且透明地升级他,那样客户端就不需要关心任何拦截了。
观察者客户端 一个简单接口提供了一个使用NMSSubject类作为参数的update方法。这个方法调用子类的getNewData()方法。
interface Observer {
void update(NMSSubject subject);
}public class NMSListener implements Observer
{public void update(NMSSubject subject) {
updateView(subject.getNewData());
}public void updateView(String data) {
System.out.println("Updated subject data is " + data);
}}
getNewData()方法能过UpgradeInterceptor类来拦截升级。让我们看一下这个类。
通过AOP升级观察者监听器 一旦我们拦截了getNewData(),他返回的数据可以通过调用JNDI命名服务来参数化。这个调用搜索名为report.txt文件。文件中的数据被返回给调用者-- NMSListener.update()。
public class UpgradeInterceptor implements Interceptor{
public Object intercept(Invocation invocation)
throws Throwable
{
String methodName = getFullMethodName(invocation);
System.out.println("Intercepted method name: " + methodName);
// Call the intercepted method.
Object result = invocation.proceed();
// Now call into JNDI to get some data
Lookup aLookup = new Lookup();
// Add this data to the intercepted method result
return (String) result + aLookup.directoryLookup("report.txt");
}}
Lookup类通过下面的代码使用命名服务来获取需要的对象(report.txt):
// Create the initial context
Context ctx = new InitialContext(env);
// Look up an object
Object obj = ctx.lookup(searchElement);
对象返回给拦截方法。
执行程序 这儿的代码是在JDK1.5.0_01下写的。下面四个AOP相关的类库需要被放在classpath路径中:
他们可以从dynaop project's Documents and Files page下载。实际上都包含在一个dynaop-1.0-beta.zip压缩文件中。记得将脚本文件(前面提到的)放在与源程序同一目录下。你还需要安装JNDI及复制report.txt到根目录下(如D:)。然后编译这些JAVA类并且用下面的命令来执行:
java ObserverTest
这应该会输出下面的内容:
Intercepted method name: NMSSubject.getNewData()
Updated subject data is Data Set: E:\report.txt
Elapsed time in seconds 2
第一行显示了拦截及调用升级代码。第二行显示了参数数据。第三行显示了运行程序所花费的时间—而不使用AOP的版本只需要0秒。这个花费是使用AOP技术的代价。
通过AOP升级 我们现在已经了解了实现基于AOP升级的所需要的所有元素。如上面的例子,我在UpgradeInterceptor类中增加了升级代码。如果我们有一个需要升级的操作系统(可能刚发现了一些严重的问题)。假设ObserverTest没有使用AOP。
为了升级ObserverTest类,我们必须复制下面的文件:
复制编译的使用AOP的ObserverTest类 复制dynaop脚本文件dynaop.bsh。
下次ObserverTest类初始化的时候,他会使用拦截,这样就方便了升级。
注意 一旦ObserverTest类使用这种方式升级,程序的代码将不再匹配编译的版本—处于潜在的混乱状态。在合适的时候建议使用完整的版本重新使代码一致。这种技术只应用在例外状况下使用。当然,在拦截过程中会有一些性能上的损失。
小结 大部分成功的软件应用倾向于承受功能的增加—尽可能地在每一次发布时增加更多的功能。一些公司还在以不知疲倦的程序员增加的代码行数作为产品成熟的标志。传统上,这有时被称为“垂直开发”—软件像伸展的摩天大楼般生长。
AOP的主要优点在于他允许水平集成新功能。这种模式有利于分离旧的代码和新的功能。此外,AOP是一种有用的技术允许拦截代码来更新接口。这对升级代码作为一个小补丁发布来说是非常有用的。如果代码坚持如概念分离这类的设计原则,那么拦截就可以非常有效地减少风险。在这个例子中,这种分离是用Observer模式来取得的,他提供了一种清晰的数据发布和订阅的分离。