Netbeans Platform: 注册与发现的机制

Netbeans Platform: 注册与发现的机制

Netbeans Platform是模块化的设计。我们看一下最基本的开发思路:

例如,我们想要写一个XML验证器模块。我们会做一下几个事情:

1. 设计Validator接口, 定义一些验证所需的方法。方便以后提供Manifest验证器,html验证器等。

2. 设计实现Validation类,这个类维护一个Validator的Map, 允许添加和删除Validatior,如果需要验证的话,这个类会遍历Validator找出响应的Validator实现。

3. 实现XMLValidator类,实现Validator接口

4. 实现XMLModuleInstall类,继承ModuleInstall类。这个类维护一个静态的Validation类,完成XMLValidator的注册和去注册任务

这里有个问题,就是上述办法包含太多的样板代码。例如Validation中要维护注册许多验证器。每个模块需要提供Validator的实现类的同时,还要编写另一个类继承ModuleInstall类来完成注册和去注册的任务, 例如XMLValidator还要XMLModuelInstall, XMLValidator负责验证任务,XMLModuelInstall负责注册和去注册XMLValidator. 通过ModuleInstall程序在启动时都要加载验证器,不管你到底用不用它。

所以,Netbeans Platform需要一个好的注册模式。

我们先看看其他已经存在的注册模式:

java.awt.Toolkit使用一种基于属性的解决方案,property-based.

public Toolkit getDefaultToolkit() {
java.awt.Toolkit t = null;
String classname =
System.getProperty("java.awt.Toolkit"); // 从系统中获得java.awt.Toolkit属性:类名称
if (classname != null) {
try {
Class c = Class.forName(classname); // 根据类名称获得响应的类
t = (java.awt.Toolkit) c.newInstance(); // 使用类创建类实例
} catch (Exception ex) {
System.out.println
("Cannot initialize toolkit: " + classname);
ex.printStackTrace();
}
}
// Fallback
if (t == null) {
t = new GenericAWTToolkit(); // 如果都不成功,返回一个通用的默认的实例
}
}

这种解决方案比以前有进步,没有烦人的setter方法和注册方法,不过它需要你在Java虚拟机运行时正确的设置属性。你不得不在程序启动时初始化。

我们看看Netbeans Platform如何做这件事情。首先,设计理念是,基于现有的标准,需要的时候作些增强,必须很好的和Netbeans运行时容器提供的动态模块环境相匹配。这种解决方案必须对任何接口和类都适合才行。并且它要支持监听器,允许任何人监听已经注册了的对象的任何变化。例如,监听一个模块的激活和失活。

Netbeans Platform如何做到这一点呢?让我们看看:Lookup!

MetaInf Service 元信息服务

JDK1.3的时候,Java提出了一个概念:服务提供者,Service Provider.这个概念介绍了一种完全是声明式的注册机制。实际上这种注册机制完全基于Java虚拟机的当前类路径,并没有什么其他的东西。这种注册机制使用Java类路径定义注册了的对象,非常容易使用。改变注册提供者,仅仅需要将提供者的jar包放在应用程序的类路径上就可以了。这样提供者能够迅速被查找到并且可以被其他代码访问。

背后的基本思想是每个希望提供某些接口实现的jar文件(Netbeans的术语就是模块),例如javax.xml.parser.DocumentBuilderFactory的实现,能够创建自己的接口实现,比如org.saxon.MyFactory. 然后通过在自己jar包中创建一个META-INF/services/javax.parser.DocumentBuilderFactory文件的方式暴露给系统,这被称为系统中的服务。这个文件中每一行包含一个实现类的名字。在这个例子中,这个文件中存在:org.saxon.MyFactory一行,文件名字就是接口,里面的内容的一行就是一个实现。

我们看一下如何运行的呢?当你使用DocumentBuilderFactory.newInstance()来创建一个实现实例的时候,系统会扫描所有的META-INF/services/javax.parser.DocumentBuilderFactory文件。如何扫描呢?通过ClassLoader.getResources("META-INF/services/javax.parser.DocumentBuilderFactory"). 系统将会读取文件的内容,通过默认构造器创建实例。究竟那一个实现会被创建呢?答案是第一个。

这种方式很好,也很流行了,在JDK1.6中,更是增加了一个辅助类java.util.ServiceLoader. 使用这个类,能更加容易的查找到所有注册的服务,不用手工编写“查找,读取,实例化”的代码。因此在JDK1.6中上述的任务简化为:

return ServiceLoader.load(DocumentBuilderFactory.class, someClassLoader).iterator().next();

OK, 让我们回到Netbeans上,Netbeans的解决方案基于上述技术。它被称为Lookup!

全局Lookup

org.openide.util.Lookup, 它和java.util.ServiceLoader一样是service provider模式。你可能要问,为什么你要单独搞出这么个东西?原因如下:

1. 如果你使用JDK1.6以前的版本(虽然不太可能),你可以使用Lookup作为替代

2. Lookup能够立刻在Netbeans运行时容器中使用。它知道如何发现系统中的模块,并且读取他们定义的服务。

3. Lookup支持监听器。客户代码可以设置监听器,监听Lookup内容的变化。这在动态环境中非常重要,模块可以在运行时激活或者失活,从而影响一系列注册服务的提供者。

4. Lookup可以扩展和替代。JDK1.6的java.util.ServiceLoader是最终类,并且是硬编码的,而Lookup是可扩展的类,允许各种实现,这在单元测试中尤其重要。甚至你可以编写一个增强版的Lookup, 让它不仅仅从META-INF/services中读取信息。

5. Lookup是通用的抽象层。每个classloader可以有一个JDK的ServiceLoader实例,而在Netbeans中可以有成千上万个相互独立的Lookup,每个可以代表一个单一的位置去发现和查询服务和接口。事实上,这正是Netbeans使用Lookup的方式:他代表每个对话,窗口元素,树中节点等的上下文。

我们重新看一下Validation的例子,在先前的例子中,Validation依靠维护一个Validator的Map进行注册和去注册。那么使用Lookup后,Validation根本不需要这个Map,代码如下:

package org.netbeans.examples.validate.api;
import org.openide.util.Lookup;
public final class Validations {
private Validations() {}

// 将Validator.class定制为一个Lookup模版 Lookup.Template,

// 通过Lookup获得Validator.class的模版查找到Lookup.Result结果

// 这个结果包含所有找到的Validator实例,遍历这些实例,找到相应的Validator实例来验证


private static Lookup.Result<Validator> validators =
Lookup.getDefault().lookup(
new Lookup.Template<Validator>(Validator.class)
);
// Or in 6.0 version just:
// Lookup.Result<Validator> validators =
// Lookup.getDefault().lookupResult(Validator.class);


public static void validate(String mimeType, InputStream is)
throws IOException {
for (Validator v : validators.allInstances()) {
if (v.supportsMimeType(mimeType)) {
v.validate(is);
return;
}
}
throw new IOException("No validator found for " + mimeType);
}
}

使用Lookup不仅省却了Validation的Map,而且不需要registerValidator等方法。全部依赖于Lookup.getDefault的实现和其注册方式。这样Validation类只需关注validate方法就可以了。

看看吧,现在如果我们想实现另一个验证器Manifest Validator. 我们不再需要知道ModuleInstall或者在模块初始化和结束化调用任何方法。我们仅仅需要声明ManifestValidator实现了Validator接口就可以了:

package org.netbeans.examples.manifestvalidator;
public final class ManifestValidator implements Validator {
public boolean supportsMimeType(String mimeType) {
return "text/x-manifest".equals(mimeType);
}
public void validate(InputStream is) throws IOException {
// Just try to read the file as manifest:
Manifest mf = new Manifest(is);
}
}

接下来怎么将ManifestValidator注册呢?很简单,在META-INF目录下创建META-INF/services/org.netbeans.examples.validate文件,告诉人们存在实现这个接口的实现,然后在这个文件中增加一行:org.netbeans.examples.manifestvalidator.ManifestValidator。告诉人们ManifestValidator实现了这个接口。万事OK了!记住啊,我们有个org.netbeans.examples.manifestvalidator包,这个包包括一个META-INF目录,这个目录下有一个以接口名字命名的文件,这个文件中有一行实现类的类名,注册完成了!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值