ServiceLoader: The Built in DI Framework You’ve Probably Never Heard Of

Nope, its been around since Java 6.

ServiceLoader provides the capability to find and instantiate registered instances of interfaces or abstract classes.

For those farmiliar with Spring it is very similar to Bean and Autowired annotations.

Lets look at an example that uses Spring and one that uses ServiceLoader.

We’ll the discuss the similarities and differences.

spring.

ioSpring Dependency InjectionFirst off lets review the way we do simple DI with Spring.

We’ll create a simple interface:public interface SimpleService { String echo(String value);}And then an implementations of the interface:import org.

springframework.

stereotype.

Component;@Componentpublic class SimpleServiceImpl implements SimpleService { public String echo(final String value) { return value; }}Notice the @Component.

This will add the class as a bean into the application context.

Finally, our main class:@SpringBootApplicationpublic class SpringExample implements CommandLineRunner { @Autowired List<SimpleService> simpleServices; public static void main(String[] args) { SpringApplication.

run(SpringExample.

class, args); } public void run(final String.

strings) throws Exception { for (SimpleService simpleService : simpleServices) { log.

info("Echo: " + simpleService.

echo(strings[0])); } }}Notice the @Autowired list of SimpleServices.

The @SpringBootApplication will automatically scan the package for components.

These are then wired into the SpringExample class when it is run.

ServiceLoader Dependency InjectionWe will utilize the same interface as the Spring example so it won’t be repeated here.

Lets instead focus on the implementation of the service:import com.

google.

auto.

service.

AutoService;@AutoService(SimpleService.

class)public class SimpleServiceImpl implements SimpleService { public String echo(final String value) { return value; }}In the implementation we “register” an instance of the service with the annotation @AutoService.

This is only needed at compile time as the annotation is used by a javac annotation processor to automatically generate the service registration file:META-INF/services/io.

github.

efenglu.

serviceLoader.

example.

SimpleServiceThe file contains a list of Classes that implement the service:io.

github.

efenglu.

serviceLoader.

example.

SimpleServiceImplThe name of the file must match the fully qualified name of the service.

The contents can have any number of implementations, each on a separate line.

The implementation classes MUST have a no-arg constructor.

You could generate the file by hand but it is much easier to use this annotation.

And the main class:public class ServiceLoaderExample { public static void main(String [] args) { final ServiceLoader<SimpleService> services = ServiceLoader.

load(SimpleService.

class); for (SimpleService service : services) { System.

out.

println("Echo: " + service.

echo(args[0])); } }}ServiceLoader.

load is called to load a ServiceLoader which can be used to get a instance of all the services.

The ServiceLoader instance is an iterable of the service type, hence the for each loop on the services variable.

Photo by Pixabay from PexelsSo what?Both implementations are relatively small.

Both can be based mainly off of annotations and are therefore fairly straight forward to use.

So why would you use ServiceLoader over something like Spring?DependenciesLets take a look at the dependency tree of our very simple Spring service loader:[INFO] ———–< io.

github.

efenglu.

serviceLoader:spring-example >———–[INFO] Building spring-example 1.

0.

X-SNAPSHOT[INFO] ——————————–[ jar ]———————————[INFO] [INFO] — maven-dependency-plugin:3.

1.

1:tree (default-cli) @ spring-example —[INFO] io.

github.

efenglu.

serviceLoader:spring-example:jar:1.

0.

X-SNAPSHOT[INFO] +- org.

slf4j:slf4j-api:jar:1.

7.

25:compile[INFO] +- org.

springframework:spring-context:jar:4.

3.

22.

RELEASE:compile[INFO] | +- org.

springframework:spring-aop:jar:4.

3.

22.

RELEASE:compile[INFO] | +- org.

springframework:spring-core:jar:4.

3.

22.

RELEASE:compile[INFO] | | – commons-logging:commons-logging:jar:1.

2:compile[INFO] | – org.

springframework:spring-expression:jar:4.

3.

22.

RELEASE:compile[INFO] +- org.

springframework.

boot:spring-boot-autoconfigure:jar:1.

5.

19.

RELEASE:compile[INFO] +- org.

springframework.

boot:spring-boot:jar:1.

5.

19.

RELEASE:compile[INFO] – org.

springframework:spring-beans:jar:4.

3.

22.

RELEASE:compileCompared to our Service loader:[INFO] io.

github.

efenglu.

serviceLoader:serviceLoader-example:jar:1.

0.

X-SNAPSHOT## Only provided dependencies for the auto service annotation[INFO] – com.

google.

auto.

service:auto-service:jar:1.

0-rc4:provided[INFO] +- com.

google.

auto:auto-common:jar:0.

8:provided[INFO] – com.

google.

guava:guava:jar:23.

5-jre:provided[INFO] +- com.

google.

code.

findbugs:jsr305:jar:1.

3.

9:provided[INFO] +- org.

checkerframework:checker-qual:jar:2.

0.

0:provided[INFO] +- com.

google.

errorprone:error_prone_annotations:jar:2.

0.

18:provided[INFO] +- com.

google.

j2objc:j2objc-annotations:jar:1.

1:provided[INFO] – org.

codehaus.

mojo:animal-sniffer-annotations:jar:1.

14:providedIf we ignore the provided dependencies, the ServiceLoader example has NO dependencies.

That’s right, it only needs Java.

Now if you are a Spring shop this isn’t really a big deal but if you are writting something that could be used in multiple different frameworks, or want a small command line application this can make a huge difference.

SpeedSpeaking of command line applications, the startup times for ServiceLoader are MUCH faster than the Spring Boot App.

This is thanks to less code to load, no scanning, no reflection, no big frameworks.

MemorySpring is not known for being great with memory.

If memory is critical to your application you should consider using ServiceLoader if you want some sort of DI capability.

Photo by Pixabay from PexelsJava ModulesOne of the key aspects to java modules was the ability to completely firewall off classes from code outside the module.

ServiceLoader is the mechanism that allows outside code to “access” internal implementations.

Java modules allow you to register services for internal implementations while still maintaining the firewall.

In fact, this is the only officially approved mechanism of supporting dependency injection with Java Modules.

Spring and most existing DI frameworks rely on reflection to discovery and wire their components up.

But this breaks down with Java Modules.

Not even reflection can see into modules (unless you let it, but why would you).

ConclusionSpring is great.

It does a lot more than ServiceLoader ever will.

But there are times when ServiceLoader is the right choice.

It’s simple, small, fast and always available.

To see the full source code for the example check out my Git Repo:efenglu/serviceLoaderExampleA guide to using ServiceLoader.

Contribute to efenglu/serviceLoaderExample development by creating an account on…github.

com.. More details

Leave a Reply