Creating Extensible Applications (The Java Tutorials - The Extension Mechanism C

  本篇文章为你整理了Creating Extensible Applications (The Java™ Tutorials >

   The Extension Mechanism > Creating and Using Extensions)()的详细内容,包含有 Creating Extensible Applications (The Java™ Tutorials >

   The Extension Mechanism > Creating and Using Extensions),希望能帮助你了解 Creating Extensible Applications (The Java™ Tutorials >

   The Extension Mechanism > Creating and Using Extensions)。

  An extensible application is one that you can extend without modifying its original code base. You can enhance its functionality with new plug-ins or modules. Developers, software vendors, and customers can add new functionality or application programming interfaces (APIs) by adding a new Java Archive (JAR) file onto the application class path or into an application-specific extension directory.

  This section describes how to create applications with extensible services, which enable you or others to provide service implementations that require no modifications to the original application. By designing an extensible application, you provide a way to upgrade or enhance specific parts of a product without changing the core application.

  One example of an extensible application is a word processor that allows the end user to add a new dictionary or spelling checker. In this example, the word processor provides a dictionary or spelling feature that other developers, or even customers, can extend by providing their own implementation of the feature.

  The following are terms and definitions important to understand extensible applications:

  
Service

   A set of programming interfaces and classes that provide access to some specific application functionality or feature. The service can define the interfaces for the functionality and a way to retrieve an implementation. In the word-processor example, a dictionary service can define a way to retrieve a dictionary and the definition of a word, but it does not implement the underlying feature set. Instead, it relies on a service provider to implement that functionality.

   Service provider interface (SPI)

   The set of public interfaces and abstract classes that a service defines. The SPI defines the classes and methods available to your application.

   Service Provider

   Implements the SPI. An application with extensible services enable you, vendors, and customers to add service providers without modifying the original application.

  
Consider how you might design a dictionary service in a word processor or editor. One way is to define a service represented by a class named DictionaryService and a service provider interface named Dictionary. The DictionaryService provides a singleton DictionaryService object. (See the section The Singleton Design Pattern for more information.) This object retrieves definitions of words from Dictionary providers. Dictionary service clients your application code retrieve an instance of this service, and the service will search, instantiate, and use Dictionary service providers.

  Although the word-processor developer would most likely provide a basic, general dictionary with the original product, the customer might require a specialized dictionary, perhaps containing legal or technical terms. Ideally, the customer is able to create or purchase new dictionaries and add them to the existing application.

  
The DictionaryServiceDemo sample shows you how to implement a Dictionary service, create Dictionary service providers that add additional dictionaries, and create a simple Dictionary service client that tests the service. This sample, which is packaged in the zip file DictionaryServiceDemo.zip, consists of the following files:

  
Note: The build directories contain the compiled class files of the Java source files contained in the src directory in the same level.

  Running the DictionaryServiceDemo Sample

  Because the zip file DictionaryServiceDemo.zip contains compiled class files, you can unzip this file to your computer and run the sample without compiling it by following these steps:

  
Download and unzip the sample code: Download and unzip the file DictionaryServiceDemo.zip to your computer. It is These steps assume that you unzipped the contents of this file into the directory C:\DictionaryServiceDemo.

  
Change the current directory to C:\DictionaryServiceDemo\DictionaryDemo and follow the step Run the Client.

  
The DictionaryServiceDemo sample includes Apache Ant build files, which are all named build.xml. The following steps show you how to use Apache Ant to compile, build, and run the DictionaryServiceDemo sample:

  
Install Apache Ant: Go to the following link to download and install Apache Ant:

   http://ant.apache.org/

   Ensure that the directory that contains the Apache Ant executable file is in your PATH environment variable so that you can run it from any directory. In addition, ensure that your JDKs bin directory, which contains the java and javac executables (java.exe and javac.exe for Microsoft Windows). is in your PATH environment variable. See

  PATH and CLASSPATH for information about setting the PATH environment variable.

  
Download and unzip the sample code: Download and unzip the file DictionaryServiceDemo.zip to your computer. These steps assume that you unzipped the contents of this file into the directory C:\DictionaryServiceDemo.

  
Compile the code: Change the current directory to C:\DictionaryServiceDemo and run the following command:

  

ant compile-all

 

   This command compiles the source code in the src directories contained in the directories DictionaryDemo, DictionaryServiceProvider, ExtendedDictionary, and GeneralDictionary, and puts the generated class files in the corresponding build directories.

  
Package the compiled Java files into JAR files: Ensure the current directory is C:\DictionaryServiceDemo and run the following command:

  

ant jar

 

   This command creates the following JAR files:

  
DictionaryDemo/dist/DictionaryDemo.jar

   DictionaryServiceProvider/dist/DictionaryServiceProvider.jar

   GeneralDictionary/dist/GeneralDictionary.jar

   ExtendedDictionary/dist/ExtendedDictionary.jar

  
Run the sample: Ensure that the directory that contains the java executable is in your PATH environment variable. See

  PATH and CLASSPATH for more information.

   Change the current directory to C:\DictionaryServiceDemo\DictionaryDemo and run the following command:

  

ant run

 

   The sample prints the following:

   book: a set of written or printed pages, usually bound with a protective cover
editor: a person who edits
xml: a document standard often used in web services, among other things
REST: an architecture style for creating, reading, updating, and deleting data that attempts to use the common vocabulary of the HTTP protocol; Representational State Transfer

  
Understanding the DictionaryServiceDemo Sample

   The following steps show you how to re-create the contents of the file DictionaryServiceDemo.zip. These steps show you how the sample works and how to run it.

  1. Define the Service Provider Interface

  The DictionaryServiceDemo sample defines one SPI, the

  Dictionary.java interface. It contains only one method:

  

 

 

  package dictionary.spi;

  public interface Dictionary {

   public String getDefinition(String word);

  

 

  The sample stores the compiled class file in the directory DictionaryServiceProvider/build.

  
2. Define the Service That Retrieves the Service Provider Implementations

  The

  DictionaryService.java class loads and accesses available Dictionary service providers on behalf of dictionary service clients:

  

 

 

  package dictionary;

  import dictionary.spi.Dictionary;

  import java.util.Iterator;

  import java.util.ServiceConfigurationError;

  import java.util.ServiceLoader;

  public class DictionaryService {

   private static DictionaryService service;

   private ServiceLoader Dictionary loader;

   private DictionaryService() {

   loader = ServiceLoader.load(Dictionary.class);

   public static synchronized DictionaryService getInstance() {

   if (service == null) {

   service = new DictionaryService();

   return service;

  
Iterator Dictionary dictionaries = loader.iterator();

   while (definition == null dictionaries.hasNext()) {

   Dictionary d = dictionaries.next();

   definition = d.getDefinition(word);

   } catch (ServiceConfigurationError serviceError) {

   definition = null;

   serviceError.printStackTrace();

   return definition;

  

 

  The sample stores the compiled class file in the directory DictionaryServiceProvider/build.

  
The

  DictionaryService class implements the singleton design pattern. This means that only a single instance of the DictionaryService class is ever created. See the section The Singleton Design Pattern for more information.

  The DictionaryService class is the dictionary service clients entry point to using any installed Dictionary service provider. Use the

  ServiceLoader.load method to retrieve the private static member DictionaryService.service, the singleton service entry point. Then the application can call the getDefinition method, which iterates through available Dictionary providers until it finds the targeted word. The getDefinition method returns null if no Dictionary instance contains the specified definition of the word.

  The dictionary service uses the ServiceLoader.load method to find the target class. The SPI is defined by the interface dictionary.spi.Dictionary, so the example uses this class as the load methods argument. The default load method searches the application class path with the default class loader.

  However, an overloaded version of this method enables you to specify

  custom class loaders if you wish. That enables you to do more

  sophisticated class searches. A particularly enthusiastic programmer

  might, for example, create a

  ClassLoader instance that can search in an application-specific subdirectory that contains

  provider JARs added during runtime. The result is an application

  that does not require a restart to access new provider classes.

  After a loader for this class exists, you can use its iterator method to access and use each provider that it finds. The getDefinition method uses a Dictionary iterator to go through the providers until it finds a definition for the specified word. The iterator method caches Dictionary instances, so successive calls require little additional processing time. If new providers have been placed into service since the last invocation, the iterator method adds them to the list.

  The

  DictionaryDemo.java class uses this service. To use the service, the application obtains a DictionaryService instance and calls the getDefinition method. If a definition is available, the application prints it. If a definition is not available, the application prints a message stating that no available dictionary carries the word.

  
A design pattern is a general solution to a common problem in software design. The idea is that the solution gets translated into code, and that code can be applied in different situations where the problem occurs. The singleton pattern describes a technique to ensure that only a single instance of a class is ever created. In essence, the technique takes the following approach: Do not let anyone outside the class create instances of the object.

  For example, the

  DictionaryService class implements the singleton pattern as follows:

  
Declares the DictionaryService constructor as private, which prevents all other classes, except DictionaryService, from creating instances of it.

   Defines the DictionaryService member variable service as static, which ensures only one instance of DictionaryService exists.

   Defines the method getInstance, which enables other classes controlled access to the DictionaryService member variable service.

  
To provide this service, you must create a

  Dictionary.java implementation. To keep things simple, create a general dictionary that defines just a few words. You can implement the dictionary with a database, a set of property files, or any other technology. The easiest way to demonstrate the provider pattern is to include all the words and definitions within a single file.

  The following code shows an implementation of the Dictionary SPI, the

  GeneralDictionary.java class. Notice that it provides a no-argument constructor and implements the getDefinition method defined by the SPI.

  

 

 

  package dictionary;

  import dictionary.spi.Dictionary;

  import java.util.SortedMap;

  import java.util.TreeMap;

  public class GeneralDictionary implements Dictionary {

   private SortedMap String, String map;

   public GeneralDictionary() {

   map = new TreeMap String, String

   map.put(

   book ,

   a set of written or printed pages, usually bound with +

   a protective cover

   map.put(

   editor ,

   a person who edits

   @Override

   public String getDefinition(String word) {

   return map.get(word);

  

 

  The sample stores the compiled class file in the directory GeneralDictionary/build. Note: You must compile the classes dictionary.DictionaryService and dictionary.spi.Dictionary before the class GeneralDictionary.

  The GeneralDictionary provider for this example defines just two words: book and editor. Obviously, a more usable dictionary would provide a more substantial list of generally used vocabulary.

  To demonstrate how multiple providers can implement the same SPI, the following code shows yet another possible provider. The

  ExtendedDictionary.java service provider is an extended dictionary containing technical terms familiar to most software developers.

  

 

 

  package dictionary;

  import dictionary.spi.Dictionary;

  import java.util.SortedMap;

  import java.util.TreeMap;

  public class ExtendedDictionary implements Dictionary {

   private SortedMap String, String map;

   public ExtendedDictionary() {

   map = new TreeMap String, String

   map.put(

   xml ,

   a document standard often used in web services, among other +

   things

   map.put(

   REST ,

   an architecture style for creating, reading, updating, +

   and deleting data that attempts to use the common +

   vocabulary of the HTTP protocol; Representational State +

   Transfer

   @Override

   public String getDefinition(String word) {

   return map.get(word);

  

 

  The sample stores the compiled class file in the directory ExtendedDictionary/build. Note: You must compile the classes dictionary.DictionaryService and dictionary.spi.Dictionary before the class ExtendedDictionary.

  It is easy to imagine customers using a complete set of Dictionary providers for their own special needs. The service loader API enables them to add new dictionaries to their application as their needs or preferences change. Because the underlying word-processor

  application is extensible, no additional coding is required for customers to use the new providers.

  4. Register Service Providers

  To register your service provider, you create a provider configuration file, which is stored in the META-INF/services directory of the service providers JAR file. The name of the configuration file is the fully qualified class name of the service provider, in which each component of the name is separated by a period (.), and nested classes are separated by a dollar sign ($).

  The provider configuration file contains the fully qualified class names of your service providers, one name per line. The file must be UTF-8 encoded. Additionally, you can include comments in the file by beginning the comment line with the number sign (#).

  For example, to register the service provider GeneralDictionary create a text file named

  dictionary.spi.Dictionary . This file contains one line:

  

 

 

  dictionary.GeneralDictionary

  

 

  Similarly, to register the service provider ExtendedDictionary create a text file named

  dictionary.spi.Dictionary . This file contains one line:

  

 

 

  dictionary.ExtendedDictionary

  

 

  5. Create a Client That Uses the Service and Service Providers

  Because developing a full word-processor application is a

  significant undertaking, this tutorial provides a simpler application that uses the DictionaryService and Dictionary SPI. The DictionaryDemo sample searches for the words book, editor, xml, and REST words from any Dictionary providers on the class path and retrieves their definitions.

  The following is the

  DictionaryDemo sample. It requests a definition of the target word from the DictionaryService instance, which passes the request to its known Dictionary providers.

  

 

 

  package dictionary;

  import dictionary.DictionaryService;

  public class DictionaryDemo {

   public static void main(String[] args) {

   DictionaryService dictionary = DictionaryService.getInstance();

   System.out.println(DictionaryDemo.lookup(dictionary, book ));

   System.out.println(DictionaryDemo.lookup(dictionary, editor ));

   System.out.println(DictionaryDemo.lookup(dictionary, xml ));

   System.out.println(DictionaryDemo.lookup(dictionary, REST ));

   public static String lookup(DictionaryService dictionary, String word) {

   String outputString = word + :

   String definition = dictionary.getDefinition(word);

   if (definition == null) {

   return outputString + Cannot find definition for this word.

   } else {

   return outputString + definition;

  

 

  The sample stores the compiled class file in the directory DictionaryDemo/build. Note: You must compile the classes dictionary.DictionaryService and dictionary.spi.Dictionary before the class DictionaryDemo.

  6. Package the Service Providers, the Service, and the Service Client in JAR Files

  See the lesson

  Packaging Programs in JAR Files for information about how to create JAR files.

  Packaging Service Providers in JAR Files

  To package the GeneralDictionary service provider, create a JAR file named GeneralDictionary/dist/GeneralDictionary.jar that contains the compiled class file of this service provider and the configuration file in the following directory structure:

  
Similarly, to package the ExtendedDictionary service provider, create a JAR file named ExtendedDictionary/dist/ExtendedDictionary.jar that contains the compiled class file of this service provider and the configuration file in the following directory structure:

  
Note that the provider configuration file must be in the directory META-INF/services in the JAR file.

  Packaging the Dictionary SPI and Dictionary Service in a JAR File

  Create a JAR file named DictionaryServiceProvider/dist/DictionaryServiceProvider.jar that contains the following files:

  
Create a JAR file named DictionaryDemo/dist/DictionaryDemo.jar that contains the following file:

  
The following command runs the DictionaryDemo sample with the GeneralDictionary service provider:

  Linux and Solaris:

  java -Djava.ext.dirs=../DictionaryServiceProvider/dist:../GeneralDictionary/dist -cp dist/DictionaryDemo.jar dictionary.DictionaryDemo

  Windows:

  java -Djava.ext.dirs=..\DictionaryServiceProvider\dist;..\GeneralDictionary\dist -cp dist\DictionaryDemo.jar dictionary.DictionaryDemo

  When using this command, the following is assumed:

  
DictionaryDemo/dist/DictionaryDemo.jar: Contains the DictionaryDemo class

   DictionaryServiceProvider/dist/DictionaryServiceProvider.jar: Contains the Dictionary SPI and the DictionaryService class

   GeneralDictionary/dist/GeneralDictionary.jar: Contains the GeneralDictionary service provider and configuration file

  
book: a set of written or printed pages, usually bound with a protective cover

  editor: a person who edits

  xml: Cannot find definition for this word.

  REST: Cannot find definition for this word.

  

 

 

  Suppose you run the following command and ExtendedDictionary/dist/ExtendedDictionary.jar exists:

  Linux and Solaris:

  java -Djava.ext.dirs=../DictionaryServiceProvider/dist:../ExtendedDictionary/dist -cp dist/DictionaryDemo.jar dictionary.DictionaryDemo

  Windows:

  java -Djava.ext.dirs=..\DictionaryServiceProvider\dist;..\ExtendedDictionary\dist -cp dist\DictionaryDemo.jar dictionary.DictionaryDemo

  The command prints the following:

  

 

 

  book: Cannot find definition for this word.

  editor: Cannot find definition for this word.

  xml: a document standard often used in web services, among other things

  REST: an architecture style for creating, reading, updating, and deleting data that attempts to use the common vocabulary of the HTTP protocol; Representational State Transfer

  

 

  The ServiceLoader Class

  The java.util.ServiceLoader class helps you find, load, and use service providers. It searches for service providers on your applications class path or in your runtime environments extensions directory. It loads them and enables your application to use the providers APIs. If you add new providers to the class path or runtime extension directory, the ServiceLoader class finds them. If your application knows the provider interface, it can find and use different implementations of that interface. You can use the first loadable instance of the interface or iterate through all the available interfaces.

  The ServiceLoader class is final, which means that you cannot make it a subclass or override its loading algorithms. You cannot, for example, change its algorithm to search for services from a different location.

  From the perspective of the ServiceLoader class, all services have a single type, which is usually a single interface or abstract class. The provider itself contains one or more concrete classes that extend the service type with an implementation specific to its purpose. The ServiceLoader class requires that the single exposed provider type has a default constructor, which requires no arguments. This enables the ServiceLoader class to easily instantiate the service providers that it finds.

  Providers are located and instantiated on demand. A service loader maintains a cache of the providers that were loaded. Each invocation of the loaders iterator method returns an iterator that first yields all of the elements of the cache, in instantiation order. The service loader then locates and instantiates any new providers, adding each one to the cache in turn. You can clear the provider cache with the reload method.

  To create a loader for a specific class, provide the class itself to the load or loadInstalled method. You can use default class loaders or provide your own ClassLoader subclass.

  The loadInstalled method searches the runtime environments extension directory of installed runtime providers. The default extension location is your runtime environments jre/lib/ext directory. You should use the extension location only for well-known, trusted providers because this location becomes part of the class path for all applications. In this article, providers do not use the extension directory but will instead depend on an application-specific class path.

  Limitations of the ServiceLoader API

  The ServiceLoader API is useful, but it has limitations. For example, it is impossible to derive a class from the ServiceLoader class, so you cannot modify its behavior. You can use custom ClassLoader subclasses to change how classes are found, but ServiceLoader itself cannot be extended. Also, the current ServiceLoader

   class cannot tell your application when new providers are available at

  runtime. Additionally, you cannot add change-listeners to the loader to

  find out whether a new provider was placed into an

  application-specific extension directory.

  
The public ServiceLoader API is available in Java SE 6.

  Although the loader service existed as early as JDK 1.3, the API was

  private and only available to internal Java runtime code.

  
Extensible applications provide service points that can be extended

  by service providers. The easiest way to create an extensible

  application is to use the ServiceLoader, which is available for Java SE 6 and later. Using this class, you can add provider

  implementations to the application class path to make new functionality

  available. The ServiceLoader class is final, so you cannot modify its abilities.

  
Previous page: Understanding Extension Class Loading

  
Next page: Making Extensions Secure

  以上就是Creating Extensible Applications (The Java™ Tutorials >

   The Extension Mechanism > Creating and Using Extensions)()的详细内容,想要了解更多 Creating Extensible Applications (The Java™ Tutorials >

   The Extension Mechanism > Creating and Using Extensions)的内容,请持续关注盛行IT软件开发工作室。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: