Netbeans平台中的模块包含两个重要的内容,一个是配置数据,一个是对象. 模块有四种方式来在Netbeans平台中安装配置数据和对象, 其中三种方式是声明式的,这种机制是我们鼓励的.
在很久很久以前, Netbeans中的大部分对象都是在启动的时候装载的. 当开发程序很小的时候,这个方式没有什么问题, 但是当你开发很大的应用的时候, 这种方式就是一场灾难. 每个系统的新的组件都让启动过长变得更加漫长,垃圾回收变得更加频繁,需要更多的内存支持.这种方式被称为程序式的安装.
不过,现在的Netbeans慢慢改变了这种方式. 大部分系统安装只涉及一个文本条目: 在XML层文件中添加一些东西,而不是运行Java代码. 理想情况下,一个模块在系统启动的时候应该不做任何事情,只有需要这个模块的时候,再做处理.这种式被称为声明式的安装.
声明式的安装是通过在模块的JAR文件的META-INF/services目录下创建一个文件或者创建一个XML层文件声明这个模块安装时需要处理的信息. 这样,当需要这个模块实际工作时,你的对象才被实例化.
当然,如果你真的需要系统启动的时候运行一些Java代码, 你可以使用ModuleInstall类.
我们知道在Netbeans平台中有四种注册/安装方式:
- 在模块的JAR文件的META-INF/services目录下增加文件条目
- 在系统文件系统下的某目录下增加文件
- 在模块的manifest文件中增加manifest条目
- 实现org.openide.modules.ModuleInstall类并且在manifest文件中配置, 这种方式是在系统启动时执行Java代码
我们知道模块的注册和安装其实在Netbeans平台中是同一个概念. 那么我们究竟应该使用何种方式进行注册呢?
具体情况具体分析:
如果我们正在实现其他模块提供的API, 那么这个模块应该告诉我们如何做. 如果它告诉你应该使用默认的lookup查询, 那么这就意味着使用META-INF/services机制.
如果我们正在定义自己开发的模块的API, 其他模块将实现这个API并且提供自己的类(想一下IDE的Navigator组件,不同的模块注册句柄能够按照不同的文件类型显示Navigator工具), 那么,很明显,基于manifest的注册机制首先就不在考虑范围之内, 因为在manifest条目中无法增加新类型. 程式化的注册机制也是能不用就不用, 因为弹性太差. 所以只剩下 META-INF/services机制或者系统文件系统机制. 我们可以考虑几个问题:
Is this just a simple interface or abstract class people should implement, and I just have to find them all and use them?
A: If so, use META-INF/services
Q: Will all of the objects other modules will register be used at the same time, or are there subsets for specific contexts?
A: This pertains to performance and scalability - wherever possible, you want to avoid actually instantiating the objects other modules install, and delay instantiating them until they really need to be used. Opening module jars and classloading are both expensive operations that slow things down.
If there will potentially be a large number of subclasses of your interface, try to find a way to divide them into context-appropriate categories and use folders in the system filesystem to partition contexts. For example, if you define the interface Eater, and modules will implement eaters of various foods, you probably should create folders for different general kinds of food. That way, you don't have to load a bunch of classes and create the OliveEater, BreadEater, EggEater and SausageEater just to ask them if they can eat the Pizza you came across.
Q: Is there information needed about how each object is to be used which could be provided declaratively, without instantiating the object?
A: If so, use the system filesystem, and either let modules declare file attributes thatprovide additional information, or let modules put their objects in subfolders that have contextual meaning (the same way the folder Loaders/text/x-java uses the folder path to indicate the data type objects pertain to).
Q: How many modules will actually implement my interface? How many objects from other modules will I typically be dealing with? Do I need to instantiate all of them just to display a list in the UI?
A: Displaying things in the UI is a particularly important case: You do not want to have to load a bunch of classes from a module just to show an icon and a display name for something - this pertains to anything you're going to display in the Options dialog, on Menus, or various other views.
You can solve this by using .instance files and asking that modules implementing your API use the file attributes defined for .instance files to declare the icon and display name. Then your code can just get a FileObject for each registered instance, call DataObject.find(theFileObject).getNodeDelegate().getIcon() or getDisplayName() to get the icon or display name of the object without ever having to create the object until it needs to do real work.
If you run the risk of having to instantiate all the objects in a folder just do trivial tests such as finding out how many objects of one class are in a given folder (which may contain objects of other classes), consider recommending .settings files instead of .instance files in your folders.