The main goal of this project is to explore services based on Java 9
modularity:
- uses
- provides X with Y;
Reference: Java 9 Modularity
- bot - exploring
usesand default service implementation - frenchman, spaniard - exploring
provides X with Y - client - mock of
GUI
- Services allow modules to provide implementations without explicitly exporting them (the module system has special privileges to reach into the provider module to instantiate the nonexported implementation class on behalf of the consumer).
- Consumers of the service can use instances of this implementation class without having access to it directly.
- The only shared type between provider and consumer is the service type (most often an interface).
- provides X with Y -
Yis implementation ofX, and is exposed externally as a service type. - uses - consuming a service in the
Java 9module system is quite straightforward: we add ausesclause tomodule-info.javaand we get all available service instances byServiceLoader.load(X.class), whereXis a service interface. - Service implementation could be provided by a module that we don’t have on the module path at compile-time (providers and consumers are bound only at run-time).
module services.bot {
requires com.google.common;
exports bot;
exports bot.factory;
uses bot.Bot;
provides bot.Bot with bot.Englishman;
}
All below examples are from BotFactory.
- Example of obtaining all available services instances of
Botinterface:return ImmutableList.copyOf(ServiceLoader.load(Bot.class)); - Instances are created for all the provider types that have been
discovered for the requested
Botinterface.
ServiceLoaderdeclaration:so simple calling:public static <S> ServiceLoader<S> load(Class<S> service)is not creating instances. You should do:ServiceLoader.load(Bot.class)ServiceLoader.load(Bot.class).stream().map(ServiceLoader.Provider::get) - From java-doc:
Returns an instance of the provider. @throws ServiceConfigurationError If the service provider cannot be instantiated, or in the case of a provider factory, the public static provider() method returns null or throws an error or exception. - Example of filtering all services to find
Botwithlanguage:public static final Bot get(String language) { return getAllBots() .stream() .filter(x -> Objects.equals(x.language(), language)) .findAny() .orElseThrow(() -> new LanguageNotSupportedException(language + " is not supported yet.")); } - Example of providing default implementation if no service is available:
public static final Bot getOrDefault(String name) { return getAllBots() .stream() .filter(x -> Objects.equals(x.language(), name)) .findAny() .orElse(new Englishman()); } - The following example loads the first available service provider.
If no service providers are located then it uses a default implementation:return ServiceLoader.load(Bot.class).findFirst().orElse(new Englishman());
Mocks GUI: displays all languages to choose and then load the Bot that
welcome you in chosen language.
module services.client {
requires services.bot;
}
As you see - we only need requires on a base module with interface
Bot.
- Service instances can be created in three ways.
Implementation class must have either:
• a public no-arg constructor
• static provider method
• factory with static provider method
Frenchman class has private constructor and public static provide()
method.
module services.frenchman {
requires services.bot;
provides bot.Bot with frenchman.Frenchman;
}
Spaniard class has private constructor and we have public factory
class with public static provide() method.
module services.spaniard {
requires services.bot;
provides bot.Bot with spaniard.Spaniard.Factory;
}