When do I use which registration method?
As described in DevFaqModulesGeneral, there are several different declarative registration mechanisms:
-
Use the
@ServiceProvider
annotation (or one of the other registration mechanisms) to register objects in the default Lookup -
Add files to folders in the system filesystem
-
Run some code on startup by implementing
ModuleInstall
and declaring yourModuleInstall
subclass in your module’smanifest.mf
If you are implementing some API from another module, that module should tell you what to do. If it tells you something should be in the default lookup, that means to use @ServiceProvider (see caveats in DevFaqWaysToRegisterInDefaultLookup).
Deciding On A Registration Mechanism For Clients Of Your Module
If you are defining an SPI in your module, and other modules will implement it and provide their own classes, provide a declarative (plain text, no code) way to register them.
Define interfaces or abstract classes, and document where subclasses can be registered (typically the default Lookup or some folder in the system filesystem).
Starting with NetBeans 6.7, you can provide annotations which other modules can use to register their objects - so registration is declarative, but it is visible in the Java source file.
If you can possibly avoid it, don’t require your module (or modules that implement your SPI) to run code on startup to programmatically register their functionality. That slows startup time and does not scale.
Below are typical registration mechanisms and patterns, and when each is useful:
What | When to Use It | How | Examples |
---|---|---|---|
Define a singleton service class (there should be only one) that should be global to the application |
You are defining a service, but another module will provide an implementation of that service |
|
|
Define an interface or abstract class and look for multiple instances of it in the default Lookup and do something with those objects |
The objects modules will register is are implementations/subclasses of a simple interface or class. Your module only needs to find all such registered objects and use them. Your module will need all of them at the same time. |
|
|
Define an interface or abstract class, and document your strategy for locating these objects in folders in the system filesystem |
The objects modules will register is are implementations/subclasses of a simple interface or class, but not all objects are needed at any given time. At any time, some may be needed, based on what the user is doing (for example, the MIME type of the file the user is editing - MIME types map easily to folder paths, e.g. |
|
If you want to add an action to the popup menu that appears when you right-click in the text editor for a |
Define a single folder in the system filesystem where objects should be registered, and optionally a factory method which will create the object. |
|
|
Examples for the cases defined under When To Use It:
. The |
Why Declarative Registration and Lazy Loading Is Important
For best performance and scalability, avoid actually instantiating the objects other modules register until the first time your code needs to call them. Avoid programmatic registration mechanisms, and delay instantiating declaratively registered objects until they really need to be used. This is for several reasons:
-
Object take up memory. Your application will use less memory and be faster if you do not create objects that you do not know you will call.
-
Java class loading happens the first time a class is needed, and loading one class can trigger loading many others. It means file I/O happens, blocking whatever thread first needs to load the class.
-
If you create objects only when your code really is going to call them, class loading and object creation still happens, but it happens in small chunks of time as things are needed, rather than causing long pauses
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.
Why Declarative Icon and Display Name Registration Is Particularly Important
Many pieces of user interface in NetBeans — almost any tree view — is a view of a folder on disk, or a folder in the system filesystem. The Services tab is such a view; the Projects tab composes several such views; the left and right sides of the first pages of the New File and New Project wizards are such views.
The need to simply show an icon and a name should not ever be the trigger for loading hundreds or even thousands of classes (bear in mind that loading your class may mean loading many other classes — and the Java Bytecode Verifier may trigger loading many more classes than you expect).
You can handle this very simply with .instance
files:
<filesystem>
<folder name="UI">
<folder name="Runtime">
<file name="MyNode.instance">
<attr name="instanceClass" stringvalue=
"org.netbeans.modules.stuff.MyNode"/>
<attr name="iconBase" stringvalue=
"org/netbeans/modules/stuff/root.png"/>
<attr name="displayName" bundlevalue=
"org.netbeans.modules.stuff.Bundle#MyNode"/>
<attr name="position" intvalue="152"/>
</file>
</folder>
</folder>
</filesystem>
and in your resource bundle file, define
MyNode=My Node
This was a serious problem in older versions of the NetBeans IDE - for example, opening the Options dialog (which used to be a tree of Nodes and a property sheet - modules that had settings provided their own Node, and you changed settings by changing properties) - simply trying to paint it for the first time originally triggered loading, literally, thousands of classes from many different JAR files.