模块的主要作用有:
- 模块间需要显示地声明(模块)依赖,而不是以前的简单的基于 classpath
- 更强的封装隔离,模块不主动导出的话无法被 import;不
open
则无法对其使用反射技术
指令
模块通过在模块根目录下的 module-info.java
中的指令来约束使用。
定义模块
最好(或则是必须,没找到规定说明) moduleName 和文件夹名称一致,否则 javac 编译时提示 module 和预期的不一致
module moduleA {
}
引入依赖
requires moduleB;
可加 transitive
来表示依赖传递,如 requires transitive moduleB;
,然后在 moduleC
中 requires moduleA;
后,那么在 moduleC
中可使用在 moduleB
中导出的类或接口。
导出 package
exports packageFullName;
表示当别的模块(A) requires
了当前模块,那么 packageFullName
这个包(及子孙包)里的 public
和 protected
的类和接口可在模块 A 里被访问使用。
可用 exports packageFullName to moduleA, moduleB
来表示仅 moduleA
和 moduleB
可访问 packageFullName
。
提供服务
provides full.name.Service with full.name.concreateService, full.name.concreateService2;
表示该模块向外提供 full.name.Service
的实现 full.name.concreateService
和 full.name.concreateService2
。
使用服务
uses full.name.Service;
表示该模块需要使用 full.name.Service
。同时,不直接依赖具体的实现,而是使用抽象类或接口。然后搭配 ServiceLoader
或别的 IoC 工具来做依赖注入,从而达到和具体实现解耦的目的。
配合反射
opens fullPackageName;
表示 fullPackageName
可被使用反射技术。也可使用 opens fullPackageName to moduleA, moduleB
限定开放反射的范围。
open module moduleD {}
表示整个模块都是开放的。
其它
java --list-modules
查看 JDK 内置的模块
编译运行
可通过 javac -d mods --module-source-path src $(find src -name "*.java")
来编译运行(多个模块),src
表示各模块所在的目录。
也可使用 javac -d dist --module-source-path src -m client
来编译指定的模块。
示例代码
src
├── sample.client
│ ├── module-info.java
│ └── test
│ └── client
│ └── Main.java
├── sample.impl
│ ├── module-info.java
│ └── test
│ └── impl
│ └── AccessImpl.java
├── sample.model
│ ├── module-info.java
│ └── test
│ └── model
│ └── Person.java
└── sample.service
├── module-info.java
└── test
└── service
└── AccessService.java
sample.model/module-info.java
module sample.model {
exports test.model;
opens test.model;
}
sample.model/test/model/Person.java
package test.model;
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{name='" + name + "'}";
}
public static void main(String args[]) {
System.out.println("Person main");
}
}
sample.service/module-info.java
module sample.service {
requires transitive sample.model;
exports test.service;
}
sample.service/test/service/AccessService.java
package test.service;
import test.model.Person;
public interface AccessService {
String getName(Person person);
}
sample.impl/module-info.java
module sample.impl {
requires sample.service;
provides test.service.AccessService with test.impl.AccessImpl;
}
sample.impl/test/impl/AccessImpl.java
package test.impl;
import test.model.Person;
import test.service.AccessService;
import java.lang.reflect.Field;
public class AccessImpl implements AccessService {
@Override
public String getName(Person person) {
try {
return extract(person);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private String extract(Person person) throws Exception {
Field field = person.getClass().getDeclaredField("name");
field.setAccessible(true);
return (String) field.get(person);
}
}
sample.client/module-info.java
module sample.client {
requires sample.service;
uses test.service.AccessService;
}
sample.client/test/client/Main.java
package test.client;
import test.model.Person;
import test.service.AccessService;
import java.util.ServiceLoader;
public class Main {
public static void main(String[] args) {
AccessService service = ServiceLoader
.load(AccessService.class)
.findFirst()
.get();
Person person = new Person("John Doe");
String name = service.getName(person);
assert name.equals("John Doe");
System.out.println(person);
}
}
注意事项
IDEA 中,由于本身有一个 module 的概念,搭配使用 Java 的 module 时,一个 IDEA 的 module 只能定义一个 module-info.java
。