Java Service Loader vs. Spring Factories Loader

Java and Spring offer loading capabilities to achieve IoC at the module level. They are pretty similar, with Spring being a little more flexible.

By  · Tutorial
Save
30.5K Views

Inversion of Control is not only possible at the class level, but at the module level. OSGi has been doing it for a long time. However, there are IoC approaches directly available in Java, as well as in Spring.

Java Service Loader

Out-of-the-box, the Java API offers a specific form of Inversion of Control. It’s implemented by the Service Loader class. It is designed to locate implementation classes of an interface on the classpath. This way allows to discover which available implementations of an interface are available on the classpath at runtime, and thus paves the way for modules designed around a clean separation between an API module — i.e. JAR, and multiple implementation modules.

This is the path chosen by the logging framework, SLF4J. SLF4J itself is just the API, while different implementations are available (_e.g. Logback, Log4J, etc.). SLF4J clients only interact with the SLF4J API, while the implementation available on the classpath takes care of the nitty-gritty details at runtime.

It’s implemented as a file located in the META-INF/services folder of a JAR. The name of the file is the fully qualified name of the interface, while its content is a list of qualified names of available implementations. For example, for an interface ch.frankel.blog.serviceloader.Foo, there must be a file named META-INF/services/ch.frankel.blog.serviceloader.Foo which content might look like this:

ch.frankel.blog.serviceloader.FooImpl1
ch.frankel.blog.serviceloader.FooImpl2


Note that the classes listed above must implement the ch.frankel.blog.serviceloader.Foo interface.

From a code perspective, it’s very straightforward:

ServiceLoader<Foo> loader = ServiceLoader.load(Foo.class);
loader.iterator();

Service Loader Spring integration

Core Spring offers an integration with the above Service Loader via the factory beans. For example, the following code assumes there will be a least of candidate implementations:

@Configuration
public class ServiceConfiguration {

    @Bean
    public ServiceListFactoryBean serviceListFactoryBean() {
        ServiceListFactoryBean serviceListFactoryBean = new ServiceListFactoryBean();
        serviceListFactoryBean.setServiceType(Foo.class);
        return serviceListFactoryBean;
    }
}

Object object = serviceListFactoryBean.getObject();


Obviously, this requires further operations to get the data in the right form (hint: it’s a linked list).

Spring Factories Loader

In parallel to the Java Service Loader, Spring offers another Inversion of Control implementation. There’s only a single property file involved, it must be named spring.factories and located under META-INF. From a code perspective, the file is read through the SpringFactoriesLoader.loadFactories()static method - yes, for Spring, it’s quite a shock.

Client code couldn’t get any simpler:

List<Foo> foos = SpringFactoriesLoader.loadFactories(Foo.class, null);


Note that the second argument is the optional class loader.

Compared to the Java Service Loader, the differences are two-fold:

  1. Whether one file format is better .i.e. more readable or more maintainable, than the other is a matter of personal taste.
  2. There’s no requirement in spring.factories for the key to be an interface and for the values to implement it. For example, Spring Boot uses this approach to handle auto-configuration beans: the key is an annotation i.e.org.springframework.boot.autoconfigure.EnableAutoConfigurationwhile values are classes annotated with @Configuration. This allows for a much more flexible design, if used wisely.

Sources for this article can be found on GitHub in Maven format.

Published at DZone with permission of Nicolas Fränkel, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.


Comments