Simple plugin system for Spring Boot

Spring Boot is possibly one of the best ways to build a production-ready application today. It gets you started on the right track in a matter of minutes.

But did you know that, although it's discretely documented, it also includes a mechanism which allows to load some external JAR files at startup?

This can be used to have a simple way of allowing extensions or plugins to an application.

The main application

The core application must be first packaged in a non default way.

The build examples below are using Gradle, but the same options are available in Maven.

apply plugin: 'spring-boot'  
springBoot {  
    layout = 'ZIP'
}

Your application is actually packaged the same way than usual (as an executable JAR) but the underneath launcher, defined in the MANIFEST.MF file, is now a PropertiesLauncher. This new launcher will allow the use of some additional startup parameters later.

Packaging a module

Now, you can define an extension as a JAR module, and you define it as Spring Boot module:

apply plugin: 'spring-boot'  
springBoot {  
    layout = 'MODULE'
}

This packages your extension as a JAR containing its dependencies as nested files. The JAR is not executable.

Now, it's very likely that your extension uses some dependencies from the application it extends. We do not want them to be packaged again in the extension and they must be excluded.

You can use exclusions based on package names to do this.

configurations {  
   moduleDependencies {
       extendsFrom configurations.runtime
       exclude group: 'com.your.application'
       exclude group: 'org.springframework'
       // ...
   }
}

apply plugin: 'spring-boot'  
springBoot {  
   layout = 'MODULE'
   customConfiguration = 'moduleDependencies'
}

Now, your extension JAR will contain only its specific dependencies.

There might be a more efficient way to exclude the application and its dependencies using Gradle. In Maven, using the provided scope might just do the trick.

Putting everything together

Given:

  • your main application packaged as application.jar
  • your extension.jar available in a extensions directory

You can now just launch:

java -Dloader.path=extensions -jar application.jar  

The PropertiesLauncher for your main application uses the loader.path system property and includes all JAR files in this directory and add them (and their nested dependencies) in the classpath.

Now, it's up to your main application to scan for its components in the classpath.

Warning

This is not a real plugin system, and while this might be enough in simple cases, this does not provide any kind of class loading isolation between your extensions. So if several extensions have different versions of the same dependencies, not provided by the main application, you might have some weird effects.

Still looking for a Spring Boot based solution on this... Maybe with the JDK9 Jigsaw project?