Dynamically loading classes which adhere to an interface
Java's Service Provider Interface (SPI) libraries allow you to load classes with public parameterless constructors dynamically based on the interfaces they implement, and it's all done through the use of META-INF/services
.
First, you'll need the interface
:
package com.example;
public interface SomeService {
String getServiceId();
String getDisplayName();
}
Then when you need them, you can load them using Java's ServiceLoader
class, which implements Iterable
:
ServiceLoader<SomeService> loader = ServiceLoader.load(SomeService.class);
for (SomeService serv : loader) {
System.out.println(serv.getDisplayName());
}
Then when you have 1 or more implementing classes on your classpath, they register themselves in META-INF/services
. So if you have the implementation:
package com.acme;
public class SomeImplementation implements SomeService {
// ...
public SomeImplementation() { ... }
// ...
}
Note that this class needs a default no-args constructor, this is not optional.
You register it with the class loader by creating a file in META-INF/services
in your classpath (such as in the root of your jar) with the following properties:
- The name of the file is the fully qualified class name of the interface, in this case, it's
com.example.SomeService
- The file contains a newline-separated list of implementations, so for the example implementation, it would contain one line:
com.acme.SomeImplementation
.
And there you go, that's it. How you build your project will determine where you put the META-INF/services
stuff. Maven, Ant, etc. all have ways of handling this. I recommend asking another question about your specific build process if you have any trouble adding these files to your build.