模块化系统(JPMS)详解
一、知识概述
Java平台模块化系统(Java Platform Module System,JPMS)是Java 9引入的重大特性,它将模块化引入Java平台。JPMS使开发者能够更好地组织代码、管理依赖、改善安全性,并实现更强的封装性。
什么是模块化?
模块化是将大型系统分解为可管理、可重用模块的编程方法。一个模块是一个独立的、可重用的软件组件,包含:
- 代码:类、接口、枚举等
- 资源:配置文件、属性文件等
- 模块描述符:module-info.java
为什么需要模块化?
| 问题 | 传统方式 | 模块化方式 |
|---|---|---|
| 类路径问题 | 所有类都在类路径,容易冲突 | 模块路径,明确依赖 |
| 封装性 | public类对所有人可见 | 只有导出的包才可见 |
| 安全性 | 反射可访问任何类 | 可控制反射访问 |
| 启动性能 | 需要扫描整个类路径 | 精确知道依赖模块 |
| 维护性 | JAR地狱问题 | 明确的依赖关系 |
JPMS核心概念
- 模块(Module):一组包的集合,有名称、依赖、导出包
- 模块描述符(module-info.java):定义模块的元数据
- 模块路径(Module Path):类似于类路径,用于定位模块
- 可读性(Readability):模块A依赖模块B,则A可读取B
- 可达性(Accessibility):类是否可访问,取决于包是否导出
二、知识点详细讲解
2.1 模块描述符
每个模块必须在根目录下有一个module-info.java文件。
基本语法
// module-info.java
module 模块名 {
// 导出包:允许其他模块访问
exports 包名;
exports 包名 to 模块名; // 限定导出
// 开放包:允许反射访问(包括私有成员)
opens 包名;
opens 包名 to 模块名; // 限定开放
// 依赖模块
requires 模块名;
requires transitive 模块名; // 传递依赖
requires static 模块名; // 编译时依赖
// 服务使用
uses 服务接口;
// 服务提供
provides 服务接口 with 实现类;
}
完整示例
/**
* 模块描述符示例
* 文件位置:src/main/java/module-info.java
*/
module com.example.myapp {
// 导出API包供其他模块使用
exports com.example.myapp.api;
// 限定导出:只导出给特定模块
exports com.example.myapp.internal to com.example.otherapp;
// 开放包:允许反射访问(用于框架如Spring、Hibernate)
opens com.example.myapp.model;
// 限定开放:只开放给特定模块
opens com.example.myapp.entity to org.hibernate.core;
// 依赖其他模块
requires java.sql; // JDBC
requires java.logging; // 日志
requires java.naming; // JNDI
// 传递依赖:依赖当前模块的模块也会自动依赖这些模块
requires transitive java.sql;
// 静态依赖:编译时需要,运行时可选
requires static java.annotation.processing;
// 服务消费:声明需要使用的服务接口
uses com.example.myapp.spi.DataProcessor;
// 服务提供:声明提供的服务实现
provides com.example.myapp.spi.DataProcessor
with com.example.myapp.impl.JsonProcessor,
com.example.myapp.impl.XmlProcessor;
}
2.2 创建模块化项目
项目结构
my-modular-app/
├── src/
│ ├── main/
│ │ └── java/
│ │ ├── module-info.java # 模块描述符
│ │ └── com/example/app/ # 源代码
│ │ ├── Main.java
│ │ ├── api/
│ │ ├── model/
│ │ └── service/
│ └── test/
│ └── java/
│ └── com/example/app/ # 测试代码
├── target/
├── pom.xml
└── README.md
示例模块:服务层
// ================== 模块1:common模块 ==================
// common/src/main/java/module-info.java
module com.example.common {
// 导出工具类
exports com.example.common.util;
exports com.example.common.exception;
// 开放模型类供ORM框架反射
opens com.example.common.model;
// 依赖
requires java.logging;
requires org.slf4j;
}
// common/src/main/java/com/example/common/util/StringUtils.java
package com.example.common.util;
public final class StringUtils {
private StringUtils() {}
public static boolean isEmpty(String str) {
return str == null || str.isEmpty();
}
public static String trim(String str) {
return str == null ? null : str.trim();
}
}
// common/src/main/java/com/example/common/model/BaseEntity.java
package com.example.common.model;
public abstract class BaseEntity {
private Long id;
private Long createTime;
private Long updateTime;
// getter/setter
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public Long getCreateTime() { return createTime; }
public void setCreateTime(Long createTime) { this.createTime = createTime; }
public Long getUpdateTime() { return updateTime; }
public void setUpdateTime(Long updateTime) { this.updateTime = updateTime; }
}
// ================== 模块2:service模块 ==================
// service/src/main/java/module-info.java
module com.example.service {
// 导出服务接口
exports com.example.service.api;
// 开放实现类供依赖注入框架
opens com.example.service.impl to spring.core, spring.beans;
// 依赖
requires com.example.common; // 依赖自己的模块
requires java.sql;
requires transitive org.slf4j; // 传递依赖
// 服务
uses com.example.service.api.DataService;
}
// service/src/main/java/com/example/service/api/DataService.java
package com.example.service.api;
import com.example.common.model.BaseEntity;
import java.util.List;
import java.util.Optional;
public interface DataService<T extends BaseEntity> {
T save(T entity);
Optional<T> findById(Long id);
List<T> findAll();
void deleteById(Long id);
}
// service/src/main/java/com/example/service/impl/DataServiceImpl.java
package com.example.service.impl;
import com.example.common.model.BaseEntity;
import com.example.service.api.DataService;
import java.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DataServiceImpl<T extends BaseEntity> implements DataService<T> {
private static final Logger log = LoggerFactory.getLogger(DataServiceImpl.class);
private final Map<Long, T> storage = new HashMap<>();
private long idSequence = 1;
@Override
public T save(T entity) {
if (entity.getId() == null) {
entity.setId(idSequence++);
}
entity.setUpdateTime(System.currentTimeMillis());
storage.put(entity.getId(), entity);
log.info("Saved entity with id: {}", entity.getId());
return entity;
}
@Override
public Optional<T> findById(Long id) {
return Optional.ofNullable(storage.get(id));
}
@Override
public List<T> findAll() {
return new ArrayList<>(storage.values());
}
@Override
public void deleteById(Long id) {
storage.remove(id);
log.info("Deleted entity with id: {}", id);
}
}
// ================== 模块3:主应用模块 ==================
// app/src/main/java/module-info.java
module com.example.app {
// 无导出,作为主应用
// 依赖
requires com.example.common;
requires com.example.service;
// 开放所有包供Spring扫描
opens com.example.app to spring.core, spring.beans, spring.context;
}
// app/src/main/java/com/example/app/Main.java
package com.example.app;
import com.example.common.util.StringUtils;
import com.example.service.api.DataService;
import com.example.service.impl.DataServiceImpl;
import com.example.common.model.BaseEntity;
public class Main {
public static void main(String[] args) {
System.out.println("=== 模块化应用示例 ===");
// 使用common模块
String test = " Hello Module ";
System.out.println("原始: '" + test + "'");
System.out.println("trim后: '" + StringUtils.trim(test) + "'");
System.out.println("isEmpty: " + StringUtils.isEmpty(test));
// 使用service模块
DataService<User> userService = new DataServiceImpl<>();
User user = new User();
user.setUsername("张三");
user.setEmail("zhangsan@example.com");
User saved = userService.save(user);
System.out.println("保存用户: " + saved);
userService.findById(1L).ifPresent(u ->
System.out.println("查询用户: " + u));
}
}
// 简单实体类
class User extends BaseEntity {
private String username;
private String email;
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
@Override
public String toString() {
return "User{id=" + getId() + ", username='" + username +
"', email='" + email + "'}";
}
}
2.3 模块的五种访问级别
/**
* 模块系统的访问级别
*
* 1. public - 公开,需要导出包
* 2. protected - 受保护,同包和子类可访问
* 3. default(package) - 包私有,同包可访问
* 4. private - 私有,类内部可访问
* 5. 模块封装 - public类在未导出包中,模块外不可访问
*/
// ========== 模块A:导出包中的公开类 ==========
// moduleA/src/main/java/module-info.java
module moduleA {
exports com.example.api; // 导出:外部可访问public类
// 不导出 com.example.internal
}
// moduleA/src/main/java/com/example/api/PublicService.java
package com.example.api;
public class PublicService {
public void publicMethod() {} // 外部可访问
protected void protectedMethod() {} // 同包+子类可访问
void packageMethod() {} // 同包可访问
private void privateMethod() {} // 类内部可访问
}
// moduleA/src/main/java/com/example/internal/InternalService.java
package com.example.internal;
public class InternalService {
public void publicMethod() {} // 虽然是public,但包未导出,模块外不可访问
}
// ========== 模块B:使用模块A ==========
// moduleB/src/main/java/module-info.java
module moduleB {
requires moduleA;
}
// moduleB/src/main/java/com/example/BService.java
package com.example;
import com.example.api.PublicService; // OK:包已导出
// import com.example.internal.InternalService; // 错误:包未导出
public class BService {
PublicService service = new PublicService();
// service.publicMethod(); // OK
// service.protectedMethod(); // 错误:BService不是子类
// service.packageMethod(); // 错误:不同包
// service.privateMethod(); // 错误:私有方法
}
2.4 opens与反射
/**
* opens关键字:允许反射访问
* 用于支持框架(Spring、Hibernate、JUnit等)
*/
// ========== 情况1:不允许反射 ==========
// moduleA/src/main/java/module-info.java
module moduleA {
exports com.example.model;
// 没有opens
}
// 模块外尝试反射
Class<?> clazz = Class.forName("com.example.model.User");
Field field = clazz.getDeclaredField("username");
field.setAccessible(true); // 编译通过,运行时报错!
// InaccessibleObjectException
// ========== 情况2:opens整个包 ==========
// moduleA/src/main/java/module-info.java
module moduleA {
exports com.example.model;
opens com.example.model; // 允许反射访问
}
// 现在可以正常反射
Class<?> clazz = Class.forName("com.example.model.User");
Field field = clazz.getDeclaredField("username");
field.setAccessible(true); // OK
field.set(user, "newValue"); // OK,即使是私有字段
// ========== 情况3:限定opens ==========
// moduleA/src/main/java/module-info.java
module moduleA {
exports com.example.model;
// 只开放给Hibernate
opens com.example.model to org.hibernate.core;
}
// Hibernate模块可以反射访问
// 其他模块仍然会被拒绝
// ========== 情况4:open module ==========
// 整个模块开放(不推荐,破坏封装)
open module moduleA {
exports com.example.api;
// 所有包都允许反射访问
}
2.5 服务加载机制
/**
* 模块化服务加载机制
* uses + provides + ServiceLoader
*/
// ========== SPI接口模块 ==========
// spi-module/src/main/java/module-info.java
module com.example.spi {
exports com.example.spi;
}
// spi-module/src/main/java/com/example/spi/MessageService.java
package com.example.spi;
public interface MessageService {
String process(String message);
String getName();
}
// ========== 服务实现模块1 ==========
// impl-module/src/main/java/module-info.java
module com.example.impl {
requires com.example.spi;
// 提供服务实现
provides com.example.spi.MessageService
with com.example.impl.JsonMessageService,
com.example.impl.XmlMessageService;
}
// impl-module/src/main/java/com/example/impl/JsonMessageService.java
package com.example.impl;
import com.example.spi.MessageService;
public class JsonMessageService implements MessageService {
@Override
public String process(String message) {
return "{\"processed\": \"" + message + "\"}";
}
@Override
public String getName() {
return "JSON";
}
}
// impl-module/src/main/java/com/example/impl/XmlMessageService.java
package com.example.impl;
import com.example.spi.MessageService;
public class XmlMessageService implements MessageService {
@Override
public String process(String message) {
return "<processed>" + message + "</processed>";
}
@Override
public String getName() {
return "XML";
}
}
// ========== 客户端模块 ==========
// client-module/src/main/java/module-info.java
module com.example.client {
requires com.example.spi;
// 声明使用服务
uses com.example.spi.MessageService;
}
// client-module/src/main/java/com/example/client/Main.java
package com.example.client;
import com.example.spi.MessageService;
import java.util.ServiceLoader;
public class Main {
public static void main(String[] args) {
// 加载所有MessageService实现
ServiceLoader<MessageService> loader = ServiceLoader.load(MessageService.class);
System.out.println("=== 可用的消息服务 ===");
for (MessageService service : loader) {
System.out.println("服务: " + service.getName());
System.out.println("处理结果: " + service.process("Hello"));
System.out.println();
}
// 查找特定服务
System.out.println("=== 查找特定服务 ===");
loader.stream()
.filter(provider -> "JSON".equals(provider.get().getName()))
.findFirst()
.ifPresent(provider -> {
MessageService service = provider.get();
System.out.println("找到JSON服务: " + service.process("Test"));
});
}
}
2.6 模块层(Module Layer)
/**
* 模块层:运行时模块管理
* 支持动态加载模块、多版本共存
*/
import java.lang.module.*;
import java.nio.file.*;
import java.util.*;
import java.util.stream.*;
public class ModuleLayerDemo {
public static void main(String[] args) throws Exception {
// ========== 1. 获取当前模块信息 ==========
System.out.println("=== 当前模块信息 ===");
ModuleLayer bootLayer = ModuleLayer.boot();
System.out.println("模块层数量: " + bootLayer.modules().size());
// 获取当前模块
Optional<Module> currentModule = bootLayer.findModule("java.base");
currentModule.ifPresent(m -> {
System.out.println("模块名: " + m.getName());
System.out.println("类加载器: " + m.getClassLoader());
System.out.println("导出包: " + m.getPackages().size());
});
// ========== 2. 列出所有模块 ==========
System.out.println("\n=== Java SE模块 ===");
bootLayer.modules().stream()
.map(Module::getName)
.filter(name -> name != null && name.startsWith("java."))
.sorted()
.limit(10)
.forEach(System.out::println);
System.out.println("...");
// ========== 3. 查找模块中的类 ==========
System.out.println("\n=== 查找类 ===");
bootLayer.findModule("java.sql").ifPresent(module -> {
System.out.println("java.sql模块包含:");
module.getPackages().forEach(pkg ->
System.out.println(" - " + pkg));
});
// ========== 4. 模块依赖检查 ==========
System.out.println("\n=== 模块依赖 ===");
Module thisModule = ModuleLayerDemo.class.getModule();
System.out.println("当前模块: " + thisModule.getName());
ModuleDescriptor descriptor = thisModule.getDescriptor();
if (descriptor != null) {
System.out.println("依赖模块:");
descriptor.requires().stream()
.map(Requires::name)
.forEach(dep -> System.out.println(" - " + dep));
}
// ========== 5. 动态添加Reads ==========
System.out.println("\n=== 动态Reads ===");
Module javaBase = bootLayer.findModule("java.base").orElseThrow();
// 动态添加读取关系
thisModule.addReads(javaBase);
System.out.println("已添加对java.base的读取");
// ========== 6. 动态导出包 ==========
System.out.println("\n=== 动态Exports ===");
// thisModule.addExports("com.example.internal", targetModule);
// thisModule.addOpens("com.example.internal", targetModule);
// ========== 7. 自定义模块层 ==========
System.out.println("\n=== 自定义模块层 ===");
// 查找模块路径
Path modulePath = Paths.get("target/modules");
if (Files.exists(modulePath)) {
ModuleFinder finder = ModuleFinder.of(modulePath);
// 创建配置
Configuration config = bootLayer.configuration()
.resolve(finder, ModuleFinder.of(), Set.of("com.example.plugin"));
// 创建模块层
ModuleLayer parent = bootLayer;
ModuleLayer pluginLayer = parent.defineModulesWithOneLoader(
config,
ClassLoader.getSystemClassLoader()
);
System.out.println("创建了新模块层,包含:");
pluginLayer.modules().forEach(m ->
System.out.println(" - " + m.getName()));
} else {
System.out.println("(模块路径不存在,跳过)");
}
// ========== 8. 服务提供者 ==========
System.out.println("\n=== 服务提供者 ===");
ServiceLoader<java.sql.Driver> drivers = ServiceLoader.load(java.sql.Driver.class);
System.out.println("已加载的JDBC驱动:");
drivers.stream()
.map(ServiceLoader.Provider::type)
.map(Class::getName)
.forEach(name -> System.out.println(" - " + name));
}
}
三、可运行Java代码示例
完整示例:模块化应用程序
项目结构
modular-app/
├── greeting-api/
│ ├── src/main/java/
│ │ ├── module-info.java
│ │ └── com/example/api/
│ │ ├── Greeter.java
│ │ └── GreetingConfig.java
│ └── pom.xml
├── greeting-impl/
│ ├── src/main/java/
│ │ ├── module-info.java
│ │ └── com/example/impl/
│ │ ├── SimpleGreeter.java
│ │ └── FormalGreeter.java
│ └── pom.xml
├── greeting-app/
│ ├── src/main/java/
│ │ ├── module-info.java
│ │ └── com/example/app/
│ │ └── Main.java
│ └── pom.xml
└── pom.xml (父POM)
源代码
// ================== API模块 ==================
// greeting-api/src/main/java/module-info.java
module com.example.greeting.api {
// 导出API包
exports com.example.api;
// 开放配置类供框架反射
opens com.example.api to spring.beans, spring.context;
}
// greeting-api/src/main/java/com/example/api/GreetingConfig.java
package com.example.api;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* 问候语配置
*/
public class GreetingConfig {
private String prefix = "";
private String suffix = "";
private boolean includeTime;
public String getPrefix() { return prefix; }
public void setPrefix(String prefix) { this.prefix = prefix; }
public String getSuffix() { return suffix; }
public void setSuffix(String suffix) { this.suffix = suffix; }
public boolean isIncludeTime() { return includeTime; }
public void setIncludeTime(boolean includeTime) { this.includeTime = includeTime; }
public String format(String message) {
StringBuilder sb = new StringBuilder();
sb.append(prefix);
sb.append(message);
if (includeTime) {
sb.append(" [").append(LocalDateTime.now()
.format(DateTimeFormatter.ofPattern("HH:mm:ss"))).append("]");
}
sb.append(suffix);
return sb.toString();
}
}
// greeting-api/src/main/java/com/example/api/Greeter.java
package com.example.api;
/**
* 问候服务接口
*/
public interface Greeter {
/**
* 生成问候语
*/
String greet(String name);
/**
* 获取问候语类型
*/
String getType();
/**
* 设置配置
*/
default void setConfig(GreetingConfig config) {
// 默认空实现
}
}
// ================== 实现模块 ==================
// greeting-impl/src/main/java/module-info.java
module com.example.greeting.impl {
// 依赖API模块
requires com.example.greeting.api;
requires java.logging;
// 提供Greeter服务实现
provides com.example.api.Greeter
with com.example.impl.SimpleGreeter,
com.example.impl.FormalGreeter;
}
// greeting-impl/src/main/java/com/example/impl/SimpleGreeter.java
package com.example.impl;
import com.example.api.Greeter;
import com.example.api.GreetingConfig;
import java.util.logging.Logger;
/**
* 简单问候实现
*/
public class SimpleGreeter implements Greeter {
private static final Logger log = Logger.getLogger(SimpleGreeter.class.getName());
private GreetingConfig config;
@Override
public String greet(String name) {
log.fine("SimpleGreeter.greet called with: " + name);
String message = "Hello, " + name + "!";
return config != null ? config.format(message) : message;
}
@Override
public String getType() {
return "SIMPLE";
}
@Override
public void setConfig(GreetingConfig config) {
this.config = config;
log.info("Config set for SimpleGreeter");
}
}
// greeting-impl/src/main/java/com/example/impl/FormalGreeter.java
package com.example.impl;
import com.example.api.Greeter;
import com.example.api.GreetingConfig;
import java.time.LocalTime;
import java.util.logging.Logger;
/**
* 正式问候实现
*/
public class FormalGreeter implements Greeter {
private static final Logger log = Logger.getLogger(FormalGreeter.class.getName());
private GreetingConfig config;
@Override
public String greet(String name) {
log.fine("FormalGreeter.greet called with: " + name);
String timeOfDay = getTimeOfDay();
String message = "Good " + timeOfDay + ", " + name;
return config != null ? config.format(message) : message;
}
@Override
public String getType() {
return "FORMAL";
}
@Override
public void setConfig(GreetingConfig config) {
this.config = config;
log.info("Config set for FormalGreeter");
}
private String getTimeOfDay() {
int hour = LocalTime.now().getHour();
if (hour < 12) return "Morning";
if (hour < 18) return "Afternoon";
return "Evening";
}
}
// ================== 应用模块 ==================
// greeting-app/src/main/java/module-info.java
module com.example.greeting.app {
// 依赖API和实现模块
requires com.example.greeting.api;
requires com.example.greeting.impl;
// 使用Greeter服务
uses com.example.api.Greeter;
}
// greeting-app/src/main/java/com/example/app/Main.java
package com.example.app;
import com.example.api.Greeter;
import com.example.api.GreetingConfig;
import java.util.ServiceLoader;
import java.util.Scanner;
/**
* 主应用程序
*/
public class Main {
public static void main(String[] args) {
System.out.println("=================================");
System.out.println(" 模块化问候应用程序");
System.out.println("=================================\n");
// 加载所有Greeter服务
ServiceLoader<Greeter> loader = ServiceLoader.load(Greeter.class);
// 列出可用的问候器
System.out.println("可用的问候器:");
loader.stream()
.map(provider -> provider.get().getType())
.forEach(type -> System.out.println(" - " + type));
System.out.println();
// 创建配置
GreetingConfig config = new GreetingConfig();
config.setPrefix(">>> ");
config.setSuffix(" <<<");
config.setIncludeTime(true);
// 设置配置给所有问候器
loader.forEach(greeter -> greeter.setConfig(config));
// 用户交互
Scanner scanner = new Scanner(System.in);
System.out.print("请输入您的名字: ");
String name = scanner.nextLine().trim();
if (name.isEmpty()) {
name = "World";
}
System.out.println("\n使用不同的问候器:\n");
// 使用所有问候器
loader.forEach(greeter -> {
System.out.printf("[%s] %s%n",
greeter.getType(),
greeter.greet(name));
});
// 选择特定问候器
System.out.print("\n选择问候器 (SIMPLE/FORMAL): ");
String choice = scanner.nextLine().trim().toUpperCase();
String finalName = name;
loader.stream()
.filter(p -> p.get().getType().equals(choice))
.findFirst()
.ifPresentOrElse(
provider -> {
Greeter greeter = provider.get();
System.out.println("\n" + greeter.greet(finalName));
},
() -> System.out.println("\n未找到该类型的问候器")
);
scanner.close();
System.out.println("\n程序结束");
}
}
完整示例:与遗留代码兼容
/**
* 模块化应用与遗留JAR的兼容处理
*/
// ================== 方式1:自动模块 ==================
// 将非模块化JAR放在模块路径上,自动成为"自动模块"
// module-info.java
module com.example.modern {
// 依赖自动模块(JAR文件名作为模块名)
requires gson; // gson-2.8.9.jar -> 模块名 gson
requires commons.lang3; // commons-lang3-3.12.0.jar -> 模块名 commons.lang3
// 自动模块会自动导出所有包,但破坏了封装性
}
// ================== 方式2:未命名模块 ==================
// 非模块化代码放在类路径上,归入"未命名模块"
// 代码在类路径上的行为:
// - 可以访问所有模块导出的包
// - 不能访问模块未导出的包
// - 不能被模块化代码直接引用(需要通过反射)
// ================== 方式3:混合模式 ==================
// module-info.java
module com.example.hybrid {
// 模块依赖
requires java.sql;
requires java.logging;
// 自动模块依赖(非模块化库)
requires jackson.databind;
// 导出
exports com.example.api;
}
// ================== 方式4:升级类路径库为模块 ==================
// 为遗留JAR添加模块描述符
// 步骤:
// 1. 创建module-info.java
// 2. 编译并添加到JAR
// 或者使用jdeps工具自动生成
/*
# 使用jdeps分析依赖
jdeps --generate-module-info . legacy-lib.jar
# 生成类似:
module legacy.lib {
exports com.legacy.api;
requires java.logging;
}
*/
// ================== 兼容性示例代码 ==================
import com.example.api.ModernService;
import com.legacy.LegacyService; // 来自自动模块
public class CompatibilityDemo {
public static void main(String[] args) {
// 使用模块化服务
ModernService modern = new ModernService();
modern.process();
// 使用遗留服务(自动模块)
LegacyService legacy = new LegacyService();
legacy.execute();
// 反射访问未命名模块的类
try {
Class<?> clazz = Class.forName("com.unnamed.LegacyClass");
Object instance = clazz.getDeclaredConstructor().newInstance();
// 只能通过反射调用方法
} catch (Exception e) {
e.printStackTrace();
}
}
}
四、实战应用场景
场景1:插件化架构
/**
* 模块化插件系统
* 核心应用 + 动态加载插件模块
*/
import java.lang.module.*;
import java.nio.file.*;
import java.util.*;
import java.util.ServiceLoader;
// ========== 核心API模块 ==========
// core-api/src/main/java/module-info.java
module com.example.plugin.api {
exports com.example.plugin.spi;
uses com.example.plugin.spi.Plugin;
}
// core-api/src/main/java/com/example/plugin/spi/Plugin.java
package com.example.plugin.spi;
public interface Plugin {
String getName();
String getVersion();
void initialize(PluginContext context);
void execute();
void shutdown();
}
// core-api/src/main/java/com/example/plugin/spi/PluginContext.java
package com.example.plugin.spi;
import java.util.Map;
public interface PluginContext {
Map<String, Object> getProperties();
void log(String message);
}
// ========== 插件实现 ==========
// plugin-hello/src/main/java/module-info.java
module com.example.plugin.hello {
requires com.example.plugin.api;
provides com.example.plugin.spi.Plugin
with com.example.plugin.hello.HelloPlugin;
}
// plugin-hello/src/main/java/com/example/plugin/hello/HelloPlugin.java
package com.example.plugin.hello;
import com.example.plugin.spi.Plugin;
import com.example.plugin.spi.PluginContext;
public class HelloPlugin implements Plugin {
private PluginContext context;
@Override
public String getName() {
return "Hello Plugin";
}
@Override
public String getVersion() {
return "1.0.0";
}
@Override
public void initialize(PluginContext context) {
this.context = context;
context.log("Hello Plugin initialized");
}
@Override
public void execute() {
context.log("Hello Plugin executing...");
System.out.println("Hello from Plugin!");
}
@Override
public void shutdown() {
context.log("Hello Plugin shutdown");
}
}
// ========== 插件管理器 ==========
package com.example.plugin.core;
import com.example.plugin.spi.Plugin;
import com.example.plugin.spi.PluginContext;
import java.lang.module.*;
import java.nio.file.*;
import java.util.*;
import java.util.concurrent.*;
public class PluginManager implements PluginContext {
private final Map<String, Plugin> plugins = new ConcurrentHashMap<>();
private final Map<String, Object> properties = new ConcurrentHashMap<>();
private final Path pluginsDir;
private ModuleLayer pluginLayer;
public PluginManager(Path pluginsDir) {
this.pluginsDir = pluginsDir;
}
/**
* 加载插件目录中的所有插件
*/
public void loadPlugins() throws Exception {
if (!Files.exists(pluginsDir)) {
System.out.println("插件目录不存在: " + pluginsDir);
return;
}
// 创建模块查找器
ModuleFinder finder = ModuleFinder.of(pluginsDir);
// 找到所有插件模块
Set<String> pluginNames = new HashSet<>();
finder.findAll().forEach(moduleRef -> {
pluginNames.add(moduleRef.descriptor().name());
});
if (pluginNames.isEmpty()) {
System.out.println("未找到插件模块");
return;
}
// 创建配置
ModuleLayer bootLayer = ModuleLayer.boot();
Configuration config = bootLayer.configuration()
.resolve(finder, ModuleFinder.of(), pluginNames);
// 创建插件层
pluginLayer = bootLayer.defineModulesWithOneLoader(
config,
getClass().getClassLoader()
);
// 加载插件服务
for (String pluginName : pluginNames) {
pluginLayer.findModule(pluginName).ifPresent(module -> {
ServiceLoader<Plugin> loader = ServiceLoader.load(
module.getLayer(),
Plugin.class
);
loader.forEach(plugin -> {
plugins.put(plugin.getName(), plugin);
plugin.initialize(this);
System.out.println("已加载插件: " + plugin.getName() +
" v" + plugin.getVersion());
});
});
}
}
/**
* 执行所有插件
*/
public void executeAll() {
plugins.values().forEach(Plugin::execute);
}
/**
* 卸载所有插件
*/
public void unloadAll() {
plugins.values().forEach(Plugin::shutdown);
plugins.clear();
}
@Override
public Map<String, Object> getProperties() {
return properties;
}
@Override
public void log(String message) {
System.out.println("[PluginLog] " + message);
}
}
// ========== 主程序 ==========
public class PluginApp {
public static void main(String[] args) throws Exception {
PluginManager manager = new PluginManager(Paths.get("plugins"));
// 设置属性
manager.getProperties().put("app.name", "Plugin Demo");
manager.getProperties().put("app.version", "1.0");
// 加载插件
System.out.println("=== 加载插件 ===");
manager.loadPlugins();
// 执行插件
System.out.println("\n=== 执行插件 ===");
manager.executeAll();
// 卸载插件
System.out.println("\n=== 卸载插件 ===");
manager.unloadAll();
}
}
场景2:安全API封装
/**
* 使用模块系统实现安全的API封装
* - 公开API对所有人可用
* - 内部实现只有授权模块可访问
* - 敏感操作需要特殊权限
*/
// ========== 安全框架模块 ==========
// security-framework/src/main/java/module-info.java
module com.example.security {
// 公开API
exports com.example.security.api;
exports com.example.security.annotation;
// 限定导出:只给授权模块
exports com.example.security.internal to
com.example.security.admin,
com.example.security.audit;
// 开放给测试框架
opens com.example.security.model to junit, org.mockito;
// 服务
uses com.example.security.spi.AuthProvider;
}
// security-framework/src/main/java/com/example/security/api/SecurityService.java
package com.example.security.api;
import com.example.security.annotation.Secure;
import java.util.Optional;
/**
* 安全服务公开API
*/
public interface SecurityService {
/**
* 认证用户
*/
AuthResult authenticate(String username, String password);
/**
* 检查权限
*/
boolean hasPermission(String userId, String resource, String action);
/**
* 执行安全操作
*/
<T> T executeSecure(SecureOperation<T> operation);
/**
* 获取当前用户
*/
Optional<UserContext> getCurrentUser();
}
// security-framework/src/main/java/com/example/security/api/AuthResult.java
package com.example.security.api;
public class AuthResult {
private final boolean success;
private final String userId;
private final String token;
private final String errorMessage;
private AuthResult(boolean success, String userId, String token, String errorMessage) {
this.success = success;
this.userId = userId;
this.token = token;
this.errorMessage = errorMessage;
}
public static AuthResult success(String userId, String token) {
return new AuthResult(true, userId, token, null);
}
public static AuthResult failure(String errorMessage) {
return new AuthResult(false, null, null, errorMessage);
}
public boolean isSuccess() { return success; }
public String getUserId() { return userId; }
public String getToken() { return token; }
public String getErrorMessage() { return errorMessage; }
}
// security-framework/src/main/java/com/example/security/api/UserContext.java
package com.example.security.api;
import java.util.Set;
public class UserContext {
private final String userId;
private final String username;
private final Set<String> roles;
public UserContext(String userId, String username, Set<String> roles) {
this.userId = userId;
this.username = username;
this.roles = roles;
}
public String getUserId() { return userId; }
public String getUsername() { return username; }
public Set<String> getRoles() { return roles; }
public boolean hasRole(String role) { return roles.contains(role); }
}
// security-framework/src/main/java/com/example/security/annotation/Secure.java
package com.example.security.annotation;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Secure {
String resource() default "";
String action() default "";
String[] roles() default {};
}
// security-framework/src/main/java/com/example/security/internal/InternalSecurityService.java
package com.example.security.internal;
import com.example.security.api.*;
import com.example.security.spi.AuthProvider;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* 内部实现(不对外公开)
*/
public class InternalSecurityService implements SecurityService {
private final AuthProvider authProvider;
private final Map<String, UserContext> sessions = new ConcurrentHashMap<>();
public InternalSecurityService(AuthProvider authProvider) {
this.authProvider = authProvider;
}
@Override
public AuthResult authenticate(String username, String password) {
// 内部实现逻辑
return authProvider.authenticate(username, password);
}
@Override
public boolean hasPermission(String userId, String resource, String action) {
// 权限检查逻辑
return authProvider.checkPermission(userId, resource, action);
}
@Override
public <T> T executeSecure(SecureOperation<T> operation) {
// 安全执行逻辑
return operation.execute();
}
@Override
public Optional<UserContext> getCurrentUser() {
// 从ThreadLocal获取当前用户
return Optional.ofNullable(sessions.get(Thread.currentThread().getName()));
}
}
// ========== 管理模块(有权限访问内部API)==========
// security-admin/src/main/java/module-info.java
module com.example.security.admin {
requires com.example.security;
// 可以访问internal包
exports com.example.admin;
}
// security-admin/src/main/java/com/example/admin/SecurityAdminController.java
package com.example.admin;
import com.example.security.internal.InternalSecurityService; // 可以导入!
public class SecurityAdminController {
private final InternalSecurityService securityService;
public SecurityAdminController(InternalSecurityService service) {
this.securityService = service;
}
public void resetSession(String userId) {
// 可以访问内部方法
securityService.clearSession(userId);
}
}
// ========== 普通应用模块(无法访问内部API)==========
// security-app/src/main/java/module-info.java
module com.example.security.app {
requires com.example.security; // 只能访问api包
}
// security-app/src/main/java/com/example/app/AppController.java
package com.example.app;
import com.example.security.api.SecurityService; // OK:公开API
import com.example.security.api.AuthResult;
// import com.example.security.internal.*; // 错误:无法访问internal包
public class AppController {
private final SecurityService securityService;
public AppController(SecurityService service) {
this.securityService = service;
}
public String login(String username, String password) {
AuthResult result = securityService.authenticate(username, password);
if (result.isSuccess()) {
return result.getToken();
}
throw new SecurityException(result.getErrorMessage());
}
}
五、总结与最佳实践
核心要点回顾
| 概念 | 关键点 |
|---|---|
| 模块描述符 | module-info.java定义模块元数据 |
| exports | 导出包,允许其他模块访问public类 |
| opens | 开放包,允许反射访问 |
| requires | 声明依赖模块 |
| uses/provides | 服务加载机制 |
| 模块层 | 运行时模块管理,支持动态加载 |
最佳实践
-
渐进式迁移
阶段1:在类路径上运行(传统方式) 阶段2:使用自动模块引入依赖 阶段3:逐步添加module-info.java 阶段4:完全模块化 -
合理的模块划分
// API模块:只包含接口和模型 module com.example.api { exports com.example.api; } // 实现模块:包含具体实现 module com.example.impl { requires com.example.api; provides com.example.api.Service with com.example.impl.ServiceImpl; } // 应用模块:组装和使用 module com.example.app { requires com.example.api; requires com.example.impl; uses com.example.api.Service; } -
最小化导出
module com.example.app { // 只导出必要的API包 exports com.example.api; // 不要导出: // - 实现包 // - 内部工具包 // - 模型包(用opens代替) } -
使用限定导出和开放
module com.example.app { // 只给框架开放 opens com.example.model to org.hibernate.core; // 只给测试模块导出 exports com.example.internal to com.example.test; } -
服务优先设计
// 优于直接依赖实现类 module com.example.client { requires com.example.api; // 只依赖API uses com.example.api.Service; // 使用服务加载 } -
处理第三方库
module com.example.app { // 第三方库作为自动模块 requires gson; // 自动模块,JAR名作为模块名 // 或者放在类路径上 }
常见问题
-
包分裂问题
// 错误:同一个包在多个模块中 module moduleA { exports com.example.util; // 包含StringUtil } module moduleB { exports com.example.util; // 包含DateUtil - 冲突! } -
循环依赖
// 错误:循环依赖 module moduleA { requires moduleB; } module moduleB { requires moduleA; // 循环! } // 解决方案:提取公共模块 module common { // 公共部分 } module moduleA { requires common; } module moduleB { requires common; } -
反射访问被拒绝
// 运行时报错:InaccessibleObjectException Field field = clazz.getDeclaredField("privateField"); field.setAccessible(true); // 如果没有opens,会抛异常 // 解决:在module-info.java中添加opens opens com.example.model to 调用模块; -
自动模块的传递依赖
module com.example.app { requires gson; // 自动模块,不传递 // 如果其他模块也需要gson,必须显式requires } // 或者 module com.example.app { requires transitive gson; // 让依赖者自动获得 }
调试技巧
# 1. 查看模块依赖树
java --show-module-resolution -jar app.jar
# 2. 使用jdeps分析依赖
jdeps --module-path mods -s myapp.jar
# 3. 列出模块内容
jar --describe-module -f mymodule.jar
# 4. 运行时诊断
java -Xdiag:resolver --module-path mods -m myapp/com.example.Main
# 5. 添加导出(调试用)
java --add-exports moduleA/com.example.internal=moduleB -jar app.jar
# 6. 打开模块(调试用)
java --add-opens moduleA/com.example.model=ALL-UNNAMED -jar app.jar
模块化迁移清单
- 分析现有依赖,绘制依赖图
- 使用jdeps检查模块兼容性
- 识别需要导出的API包
- 识别需要开放的模型包
- 创建module-info.java
- 处理第三方依赖(自动模块或类路径)
- 测试反射访问点
- 测试服务加载点
- 更新构建脚本(Maven/Gradle)
- 验证运行时行为
扩展阅读
- JPMS官方文档:JEP 261
- 模块化最佳实践:《Java 9 Modularity》
- 迁移指南:Oracle官方迁移指南
- jdeps工具:
jdeps --help - jlink工具:创建自定义运行时镜像