Order
This paper mainly studies how to build java9 multi module and service instance in maven.
maven
The whole project is the same as the traditional maven multi-module project structure. One module of java9 corresponds to one module of maven project. The following is the pom file under the root directory:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>java9-service-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<modules>
<module>consumer-demo</module>
<module>service-sort</module>
<module>service-sort-bubble</module>
<module>service-sort-merge</module>
</modules>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!--让intellij能够正确编译java9,不然老是变回使用1.5-->
<maven.compiler.source>9</maven.compiler.source>
<maven.compiler.target>9</maven.compiler.target>
</properties>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<release>9</release>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
A maven-compiler-plugin is managed here, and the configuration release is 9, because java9 supports multi release and can support multiple java versions at the same time, which is compiled into java9 version here.
service-sort
This is the service interface module.
module service.sort {
exports service.sort;
uses service.sort.SortService;
}
At the same time, it is declared here that uses SortService means that it needs to use ServiceLoader to load service instances in this module.
public interface SortService {
public <T extends Comparable> List<T> sortList(List<T> list);
public static SortService getProviderInstanceLazy() {
Stream<Provider<SortService>> providers = ServiceLoader.load(SortService.class)
.stream();
//provider方法等到get的时候才会实例化
SortService service = providers.map(Provider::get)
.findAny()
.orElse(null);
return service;
}
}
At the same time of declaring the interface, static methods are added to load the service instance.
service-sort-bubble
- maven
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>java9-service-demo</artifactId>
<groupId>com.example</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>service-sort-bubble</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>service-sort</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
Add dependency on api package here
- module-info.java
module service.sort.bubble {
requires service.sort;
provides service.sort.SortService with sort.impl.bubble.BubbleSort;
}
It is stated here that BubbleSort provides the implementation of SortService.
- BubbleSort
public class BubbleSort implements SortService {
public <T extends Comparable> List<T> sortList(List<T> list) {
System.out.println("use BubbleSort");
for (int outer = 0; outer < list.size() - 1; outer++) {
for (int inner = 0; inner < list.size()-outer-1; inner++) {
if (list.get(inner).compareTo(list.get(inner + 1)) > 0) {
swap(list, inner);
}
}
}
return list;
}
private <T> void swap(List<T>list, int inner) {
T temp = list.get(inner);
list.set(inner, list.get(inner + 1));
list.set(inner + 1, temp);
}
}
service-sort-merge
- maven
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>java9-service-demo</artifactId>
<groupId>com.example</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>service-sort-merge</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>service-sort</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
- module-info.java
module service.sort.merge {
requires service.sort;
provides service.sort.SortService with sort.impl.merge.MergeSort;
}
MergeSort is declared here as the implementation of the SortService interface.
- MergeSort
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Arrays;
import service.sort.SortService;
public class MergeSort implements SortService {
public <T extends Comparable> List<T> sortList(List<T> list) {
System.out.println("using MergeSort");
Collections.sort(list);
return list;
}
}
consumer
- maven
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>java9-service-demo</artifactId>
<groupId>com.example</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>consumer-demo</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>service-sort</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
Note that there are no dependencies to add implementation classes here
- module-info.java
module consumer {
requires service.sort;
}
- Main
public class Main {
public static void main(String[] args) {
System.out.println("sort service consumer started.");
List<Integer> data = new ArrayList<Integer>();
data.add(5);
data.add(3);
data.add(10);
data.add(2);
data.add(8);
SortService sortService = SortService.getProviderInstanceLazy();
if (sortService != null) {
sortService.sortList(data);
}
System.out.println(data);
System.out.println("finish");
}
}
Compile and run
- Compile
mvn clean install
This is done under the root directory.
- Use bubble
java --module-path ./consumer-demo/target/consumer-demo-0.0.1-SNAPSHOT.jar:./service-sort/target/service-sort-0.0.1-SNAPSHOT.jar:./service-sort-bubble/target/service-sort-bubble-0.0.1-SNAPSHOT.jar --module consumer/consumer.Main
Note that bubble’s jar has been added to module-path here.
Output
sort service consumer started.
use BubbleSort
[2, 3, 5, 8, 10]
finish
- Use merge
java --module-path ./consumer-demo/target/consumer-demo-0.0.1-SNAPSHOT.jar:./service-sort/target/service-sort-0.0.1-SNAPSHOT.jar:./service-sort-merge/target/service-sort-merge-0.0.1-SNAPSHOT.jar --module consumer/consumer.Main
Note that the jar of merge has been added to the module-path here.
Output
sort service consumer started.
using MergeSort
[2, 3, 5, 8, 10]
finish
- Both service implementations are added
java --module-path ./consumer-demo/target/consumer-demo-0.0.1-SNAPSHOT.jar:./service-sort/target/service-sort-0.0.1-SNAPSHOT.jar:./service-sort-bubble/target/service-sort-bubble-0.0.1-SNAPSHOT.jar:./service-sort-merge/target/service-sort-merge-0.0.1-SNAPSHOT.jar --module consumer/consumer.Main
Or ..
java --module-path ./consumer-demo/target/consumer-demo-0.0.1-SNAPSHOT.jar:./service-sort/target/service-sort-0.0.1-SNAPSHOT.jar:./service-sort-merge/target/service-sort-merge-0.0.1-SNAPSHOT.jar:./service-sort-bubble/target/service-sort-bubble-0.0.1-SNAPSHOT.jar --module consumer/consumer.Main
Output
sort service consumer started.
use BubbleSort
[2, 3, 5, 8, 10]
finish
The discovery seems to have nothing to do with the order in which it was added to path. Even if merge’s jar package is put in front of it, bubble is also used.
Summary
ServiceLoader already existed in java6, but at that time it was dependent on creating a full path name file for the service interface in the META-INF/services directory of the jar package, in which the full path name of the implementation class was written. Java9 also supports declaring service provider and consumer information in module-info.java after introducing modularization, so that the module system can support ServiceLoader without using the original META-INF declaration method.