With interface oriented
concept, we should put our focus on the service it provided. Say HttpServletRequest
from jakarta.servlet-api, we have different implementation like tomcat, jetty and undertow etc. Both of them has HttpServletRequestImpl
which implements HttpServletRequest
interface. Most of time, we should invoke the service provided by the interface and does NOT care about its underlying.
Another example is the JDBC. We use java.sql.DriverManager
to find and load implementations of java.sql.Driver
. In our program, we did NOT know which underlying database will be connected. But only when they are in the classpath, we can dynamic load them in our Java program with the java.sql.Driver
interface. This is called Service Provider Interface, aka SPI, in Java.
SPI enables developers to extend applications functionalities by adding services implementation, e.g. Jar files, to classpath without modifying the application code base.
Define the Service API
1 | package tk.wangkexiong.api; |
And the wrapper with the set of services provided to application
1 | package tk.wangkexiong.api; |
Add build.gradle
to package jar file
1 | plugins { |
Use the service in our application
1 | package tk.wangkexiong; |
And the main entrance
1 | public class App { |
Add build.gradle
to package jar
1 | plugins { |
Service Implementation by vendor
1 | import tk.wangkexiong.api.IMyService; |
And the wrapper:
1 | package tk.wangkexiong.impl; |
And the most important, follow SPI specification to add file named tk.wangkexiong.api.IMyServiceProvider
under META-INF/services
with content:
1 | tk.wangkexiong.impl.MyServiceProvider |
Add build.gradle
to package jar file. If you followed the project file structure of standard Java project, which means above META-INF
directory under src/main/resources
, the :jar
task will include above META-INF
information. This will make the Java SPI package.
1 | plugins { |
Run the application
1 | $ gradlew :service-api:jar |
Source code available at github