引言:模块化革命的必要性
在Java 9之前,Java生态系统面临着诸多挑战:庞大的类路径、版本冲突、复杂的依赖管理等。Java Platform Module System (JPMS) 的引入,标志着Java正式进入了模块化时代。这不仅改变了Java平台的内部结构,也彻底改变了我们构建和组织大型应用的方式。
一、模块化基础:从类路径到模块路径
1.1 模块的基本概念与结构
java
// 1. 模块定义文件:module-info.java
// 这是模块系统的核心,每个模块都必须有module-info.java文件
/**
* module-info.java示例
*
* 基本语法:
* module 模块名 {
* requires 其他模块; // 声明依赖
* exports 包名; // 导出包(公开API)
* opens 包名; // 开放包(允许反射访问)
* provides 服务 with 实现; // 提供服务实现
* uses 服务; // 使用服务
* }
*/
// 示例:一个用户服务模块的定义
module com.example.userservice {
// 依赖声明
requires java.base; // 隐式依赖,可省略
requires java.sql;
requires transitive org.slf4j; // 传递性依赖
requires static lombok; // 编译时依赖
// 导出包:这些包可以被其他模块访问
exports com.example.userservice.api;
exports com.example.userservice.domain to com.example.app;
// 开放包:允许通过反射访问(用于框架如Spring、Hibernate)
opens com.example.userservice.internal;
opens com.example.userservice.domain to spring.core, hibernate.core;
// 服务提供声明
provides com.example.userservice.spi.UserProvider
with com.example.userservice.provider.DefaultUserProvider;
// 服务使用声明
uses com.example.userservice.spi.UserProvider;
}
1.2 构建第一个模块化应用
bash
# 项目结构示例:
# modular-app/
# ├── module-a/
# │ ├── src/
# │ │ └── com.example.modulea/
# │ │ └── ModuleAClass.java
# │ └── module-info.java
# ├── module-b/
# │ ├── src/
# │ │ └── com.example.moduleb/
# │ │ └── ModuleBClass.java
# │ └── module-info.java
# └── app/
# ├── src/
# │ └── com.example.app/
# │ └── MainApp.java
# └── module-info.java
java
// Module A: 提供服务接口
module com.example.modulea {
exports com.example.modulea.api;
}
package com.example.modulea.api;
public interface GreetingService {
String greet(String name);
}
// Module B: 提供服务实现
module com.example.moduleb {
requires com.example.modulea; // 依赖Module A
provides com.example.modulea.api.GreetingService
with com.example.moduleb.impl.EnglishGreetingService;
}
package com.example.moduleb.impl;
import com.example.modulea.api.GreetingService;
public class EnglishGreetingService implements GreetingService {
public String greet(String name) {
return "Hello, " + name + "!";
}
}
// 主应用模块
module com.example.app {
requires com.example.modulea; // 使用服务接口
uses com.example.modulea.api.GreetingService;
}
package com.example.app;
import com.example.modulea.api.GreetingService;
import java.util.ServiceLoader;
public class MainApp {
public static void main(String[] args) {
// 通过ServiceLoader发现服务实现
ServiceLoader<GreetingService> loader =
ServiceLoader.load(GreetingService.class);
for (GreetingService service : loader) {
System.out.println(service.greet("World"));
}
}
}
二、模块依赖与版本管理
2.1 模块依赖解析与冲突解决
java
// 复杂的模块依赖场景
module com.example.application {
requires com.example.utils; // 依赖工具模块
requires com.example.persistence; // 依赖持久化模块
requires com.example.security; // 依赖安全模块
// 可选的动态依赖
requires static com.example.experimental;
// 排除传递依赖冲突
requires com.example.database;
// 如果com.example.database同时依赖了org.slf4j的不同版本
// 可以使用--patch-module或升级模块化解决
}
// 模块版本管理策略
/**
* 模块化系统的版本管理方式:
* 1. 模块名称本身包含版本信息(不推荐)
* 2. 使用module-info.java中的版本属性(Java 9+)
* 3. 使用构建工具(Maven/Gradle)管理版本
* 4. 使用JLink创建自定义运行时映像
*/
// module-info.java中的版本信息
@Version("1.2.0")
@Deprecated(since="2.0", forRemoval=true)
module com.example.legacy {
requires transitive java.desktop version "11+";
exports com.example.legacy.api;
}
// 构建多版本JAR(Multi-Release JAR)
// META-INF/MANIFEST.MF中声明:
// Multi-Release: true
// 目录结构:
// mymodule.jar
// ├── com/example/MyClass.class # 主要类文件(Java 8)
// ├── META-INF/
// │ └── versions/
// │ └── 9/
// │ └── com/example/MyClass.class # Java 9版本
// │ └── 11/
// │ └── com/example/MyClass.class # Java 11版本
// └── module-info.class # 模块描述符
2.2 模块化依赖冲突解决实战
java
import java.lang.module.*;
import java.util.*;
public class ModuleDependencyResolver {
// 模拟模块依赖图
static class ModuleGraph {
Map<String, ModuleNode> modules = new HashMap<>();
class ModuleNode {
String name;
String version;
Set<ModuleNode> requires = new HashSet<>();
Set<ModuleNode> requiredBy = new HashSet<>();
ModuleNode(String name, String version) {
this.name = name;
this.version = version;
}
void addDependency(ModuleNode dependency) {
requires.add(dependency);
dependency.requiredBy.add(this);
}
}
// 添加模块
ModuleNode addModule(String name, String version) {
String key = name + ":" + version;
return modules.computeIfAbsent(key, k -> new ModuleNode(name, version));
}
// 解析依赖冲突
List<Conflict> resolveConflicts() {
List<Conflict> conflicts = new ArrayList<>();
Map<String, List<ModuleNode>> nameToNodes = new HashMap<>();
// 按模块名分组
for (ModuleNode node : modules.values()) {
nameToNodes.computeIfAbsent(node.name, k -> new ArrayList<>())
.add(node);
}
// 检查每个模块的多个版本
for (Map.Entry<String, List<ModuleNode>> entry : nameToNodes.entrySet()) {
if (entry.getValue().size() > 1) {
Conflict conflict = new Conflict(entry.getKey(), entry.getValue());
conflicts.add(conflict);
}
}
return conflicts;
}
}
static class Conflict {
String moduleName;
List<ModuleGraph.ModuleNode> versions;
Conflict(String moduleName, List<ModuleGraph.ModuleNode> versions) {
this.moduleName = moduleName;
this.versions = versions;
}
@Override
public String toString() {
return String.format("模块 %s 有 %d 个版本: %s",
moduleName, versions.size(),
versions.stream()
.map(v -> v.version)
.collect(java.util.stream.Collectors.joining(", ")));
}
}
public static void main(String[] args) {
System.out.println("=== 模块依赖冲突解析 ===\n");
ModuleGraph graph = new ModuleGraph();
// 模拟一个复杂的依赖场景
ModuleGraph.ModuleNode app = graph.addModule("com.example.app", "1.0");
ModuleGraph.ModuleNode utilsV1 = graph.addModule("com.example.utils", "1.0");
ModuleGraph.ModuleNode utilsV2 = graph.addModule("com.example.utils", "2.0");
ModuleGraph.ModuleNode database = graph.addModule("com.example.database", "1.0");
ModuleGraph.ModuleNode security = graph.addModule("com.example.security", "1.0");
// 建立依赖关系
app.addDependency(utilsV1);
app.addDependency(database);
database.addDependency(utilsV2);
security.addDependency(utilsV1);
// 解析冲突
List<Conflict> conflicts = graph.resolveConflicts();
System.out.println("发现的依赖冲突:");
conflicts.forEach(System.out::println);
System.out.println("\n可能的解决方案:");
System.out.println("1. 升级所有模块使用相同版本的com.example.utils");
System.out.println("2. 使用模块重命名(--patch-module)");
System.out.println("3. 使用自定义类加载器隔离");
System.out.println("4. 重构模块避免传递依赖");
// 演示模块层(ModuleLayer)实现隔离
System.out.println("\n=== 使用ModuleLayer实现隔离 ===");
demonstrateModuleLayer();
}
private static void demonstrateModuleLayer() {
try {
// 创建父层(基础模块)
ModuleLayer parent = ModuleLayer.boot();
// 配置模块查找器
Path modulesDir = Paths.get("modules");
ModuleFinder finder = ModuleFinder.of(modulesDir);
// 解析模块
Configuration config = parent.configuration()
.resolve(finder, ModuleFinder.of(), Set.of("com.example.app"));
// 创建新的模块层
ModuleLayer layer = parent.defineModulesWithOneLoader(
config, ClassLoader.getSystemClassLoader());
// 在新层中查找模块
Optional<Module> appModule = layer.findModule("com.example.app");
if (appModule.isPresent()) {
System.out.println("成功加载模块: " + appModule.get().getName());
// 加载模块中的类
Class<?> mainClass = Class.forName(
appModule.get(), "com.example.app.Main");
// 调用方法
Method mainMethod = mainClass.getMethod("main", String[].class);
mainMethod.invoke(null, (Object) new String[]{});
}
} catch (Exception e) {
System.err.println("模块层创建失败: " + e.getMessage());
}
}
}
三、服务加载器与SPI增强
3.1 模块化服务提供者接口
java
// 服务提供者接口定义模块
module com.example.storage.spi {
exports com.example.storage.spi;
// 定义服务接口
public interface StorageService {
void save(String key, byte[] data);
byte[] load(String key);
boolean exists(String key);
}
}
// 文件存储实现模块
module com.example.storage.file {
requires com.example.storage.spi;
provides com.example.storage.spi.StorageService
with com.example.storage.file.FileStorageService;
// 实现类
class FileStorageService implements StorageService {
private final Path basePath;
public FileStorageService() {
this.basePath = Paths.get("storage");
Files.createDirectories(basePath);
}
public void save(String key, byte[] data) {
Path filePath = basePath.resolve(key);
Files.write(filePath, data);
}
public byte[] load(String key) {
Path filePath = basePath.resolve(key);
return Files.readAllBytes(filePath);
}
public boolean exists(String key) {
return Files.exists(basePath.resolve(key));
}
}
}
// 数据库存储实现模块
module com.example.storage.database {
requires com.example.storage.spi;
requires java.sql;
provides com.example.storage.spi.StorageService
with com.example.storage.database.DatabaseStorageService;
// 实现类
class DatabaseStorageService implements StorageService {
private final Connection connection;
public DatabaseStorageService() throws SQLException {
this.connection = DriverManager.getConnection("jdbc:h2:mem:test");
// 初始化表结构
try (Statement stmt = connection.createStatement()) {
stmt.execute("CREATE TABLE storage (key VARCHAR PRIMARY KEY, data BLOB)");
}
}
public void save(String key, byte[] data) throws SQLException {
String sql = "MERGE INTO storage(key, data) VALUES(?, ?)";
try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
pstmt.setString(1, key);
pstmt.setBytes(2, data);
pstmt.executeUpdate();
}
}
public byte[] load(String key) throws SQLException {
String sql = "SELECT data FROM storage WHERE key = ?";
try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
pstmt.setString(1, key);
ResultSet rs = pstmt.executeQuery();
if (rs.next()) {
return rs.getBytes("data");
}
}
return null;
}
public boolean exists(String key) throws SQLException {
String sql = "SELECT 1 FROM storage WHERE key = ?";
try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
pstmt.setString(1, key);
ResultSet rs = pstmt.executeQuery();
return rs.next();
}
}
}
}
// 应用模块使用服务
module com.example.application {
requires com.example.storage.spi;
uses com.example.storage.spi.StorageService;
// 动态服务加载器
public class StorageManager {
private final List<StorageService> services = new ArrayList<>();
public StorageManager() {
// 加载所有可用的存储服务
ServiceLoader<StorageService> loader =
ServiceLoader.load(StorageService.class);
for (StorageService service : loader) {
services.add(service);
System.out.println("加载存储服务: " + service.getClass().getName());
}
if (services.isEmpty()) {
throw new IllegalStateException("未找到可用的存储服务");
}
}
// 使用第一个可用的服务
public StorageService getDefaultService() {
return services.get(0);
}
// 根据条件选择服务
public Optional<StorageService> findService(Predicate<StorageService> predicate) {
return services.stream().filter(predicate).findFirst();
}
}
}
3.2 增强型服务注册与发现
java
import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;
// 增强的服务注册表
public class EnhancedServiceRegistry {
// 服务描述符
static class ServiceDescriptor<T> {
private final Class<T> serviceType;
private final String name;
private final String version;
private final int priority; // 优先级,数值越小优先级越高
private final Map<String, String> metadata;
ServiceDescriptor(Class<T> serviceType, String name,
String version, int priority,
Map<String, String> metadata) {
this.serviceType = serviceType;
this.name = name;
this.version = version;
this.priority = priority;
this.metadata = Map.copyOf(metadata);
}
public Class<T> getServiceType() { return serviceType; }
public String getName() { return name; }
public String getVersion() { return version; }
public int getPriority() { return priority; }
public Map<String, String> getMetadata() { return metadata; }
}
// 服务实例包装器
static class ServiceInstance<T> {
private final ServiceDescriptor<T> descriptor;
private final T instance;
private final long registrationTime;
private volatile long lastUsedTime;
ServiceInstance(ServiceDescriptor<T> descriptor, T instance) {
this.descriptor = descriptor;
this.instance = instance;
this.registrationTime = System.currentTimeMillis();
this.lastUsedTime = registrationTime;
}
public T getInstance() {
lastUsedTime = System.currentTimeMillis();
return instance;
}
public ServiceDescriptor<T> getDescriptor() { return descriptor; }
public long getAge() { return System.currentTimeMillis() - registrationTime; }
public long getIdleTime() { return System.currentTimeMillis() - lastUsedTime; }
}
// 模块感知的服务注册表
public static class ModuleAwareServiceRegistry {
private final Map<Class<?>, List<ServiceInstance<?>>> registry = new ConcurrentHashMap<>();
private final Map<String, Module> moduleCache = new ConcurrentHashMap<>();
// 注册服务(自动发现模块信息)
public <T> void register(Class<T> serviceType, T instance) {
register(serviceType, instance, createDescriptor(serviceType, instance));
}
public <T> void register(Class<T> serviceType, T instance,
ServiceDescriptor<T> descriptor) {
ServiceInstance<T> serviceInstance = new ServiceInstance<>(descriptor, instance);
registry.computeIfAbsent(serviceType, k -> new CopyOnWriteArrayList<>())
.add(serviceInstance);
// 按优先级排序
registry.get(serviceType).sort(
Comparator.comparingInt(si -> ((ServiceInstance<T>)si).getDescriptor().getPriority())
);
System.out.printf("注册服务: %s [%s v%s]%n",
serviceType.getSimpleName(),
descriptor.getName(),
descriptor.getVersion());
}
// 自动创建服务描述符
private <T> ServiceDescriptor<T> createDescriptor(Class<T> serviceType, T instance) {
Module module = instance.getClass().getModule();
String moduleName = module.getName();
// 尝试从模块注解获取元数据
String name = moduleName != null ? moduleName : serviceType.getSimpleName();
String version = "1.0.0";
int priority = 100;
if (module != null && module.isNamed()) {
// 检查模块注解
Optional<Object> moduleVersion = module.getDescriptor().version();
if (moduleVersion.isPresent()) {
version = moduleVersion.get().toString();
}
}
Map<String, String> metadata = new HashMap<>();
metadata.put("module", moduleName != null ? moduleName : "unnamed");
metadata.put("class", instance.getClass().getName());
return new ServiceDescriptor<>(serviceType, name, version, priority, metadata);
}
// 获取服务(支持过滤和选择策略)
public <T> Optional<T> getService(Class<T> serviceType) {
return getService(serviceType, descriptor -> true);
}
public <T> Optional<T> getService(Class<T> serviceType,
Predicate<ServiceDescriptor<T>> filter) {
return getService(serviceType, filter, instances -> {
// 默认选择策略:最高优先级
return instances.stream()
.min(Comparator.comparingInt(si -> si.getDescriptor().getPriority()))
.orElse(null);
});
}
public <T> Optional<T> getService(Class<T> serviceType,
Predicate<ServiceDescriptor<T>> filter,
Function<List<ServiceInstance<T>>,
ServiceInstance<T>> selector) {
List<ServiceInstance<?>> instances = registry.get(serviceType);
if (instances == null || instances.isEmpty()) {
return Optional.empty();
}
@SuppressWarnings("unchecked")
List<ServiceInstance<T>> typedInstances = (List<ServiceInstance<T>>) instances;
// 应用过滤器
List<ServiceInstance<T>> filtered = typedInstances.stream()
.filter(si -> filter.test(si.getDescriptor()))
.collect(Collectors.toList());
if (filtered.isEmpty()) {
return Optional.empty();
}
// 应用选择策略
ServiceInstance<T> selected = selector.apply(filtered);
return selected != null ?
Optional.of(selected.getInstance()) : Optional.empty();
}
// 获取所有服务实例
public <T> List<T> getAllServices(Class<T> serviceType) {
List<ServiceInstance<?>> instances = registry.get(serviceType);
if (instances == null) {
return Collections.emptyList();
}
@SuppressWarnings("unchecked")
List<ServiceInstance<T>> typedInstances = (List<ServiceInstance<T>>) instances;
return typedInstances.stream()
.map(ServiceInstance::getInstance)
.collect(Collectors.toList());
}
// 动态服务发现(通过ServiceLoader)
public <T> void discoverServices(Class<T> serviceType) {
ServiceLoader<T> loader = ServiceLoader.load(serviceType);
for (T service : loader) {
register(serviceType, service);
}
}
// 按模块卸载服务
public void unloadServicesFromModule(String moduleName) {
for (List<ServiceInstance<?>> services : registry.values()) {
services.removeIf(service -> {
String serviceModule = service.getDescriptor()
.getMetadata().get("module");
return moduleName.equals(serviceModule);
});
}
System.out.println("已卸载模块 " + moduleName + " 的所有服务");
}
// 健康检查和清理
public void cleanup(long maxIdleTimeMs) {
long cleaned = 0;
for (List<ServiceInstance<?>> services : registry.values()) {
cleaned += services.removeIf(
service -> service.getIdleTime() > maxIdleTimeMs
);
}
if (cleaned > 0) {
System.out.println("清理了 " + cleaned + " 个空闲服务");
}
}
}
public static void main(String[] args) {
System.out.println("=== 增强型服务注册表演示 ===\n");
ModuleAwareServiceRegistry registry = new ModuleAwareServiceRegistry();
// 定义服务接口
interface Calculator {
double calculate(double a, double b);
String getName();
}
// 注册一些服务实现
registry.register(Calculator.class, new Calculator() {
public double calculate(double a, double b) { return a + b; }
public String getName() { return "加法计算器"; }
});
registry.register(Calculator.class, new Calculator() {
public double calculate(double a, double b) { return a * b; }
public String getName() { return "乘法计算器"; }
});
// 使用自定义描述符注册
ServiceDescriptor<Calculator> advancedDesc = new ServiceDescriptor<>(
Calculator.class,
"科学计算器",
"2.0.0",
50, // 更高优先级
Map.of("category", "scientific", "author", "MathLab")
);
registry.register(Calculator.class, new Calculator() {
public double calculate(double a, double b) { return Math.pow(a, b); }
public String getName() { return "幂运算计算器"; }
}, advancedDesc);
// 获取默认服务(最高优先级)
Optional<Calculator> defaultCalc = registry.getService(Calculator.class);
defaultCalc.ifPresent(calc -> {
System.out.println("默认计算器: " + calc.getName());
System.out.println("计算 2^3 = " + calc.calculate(2, 3));
});
// 使用过滤器获取特定服务
Optional<Calculator> scientificCalc = registry.getService(
Calculator.class,
desc -> "scientific".equals(desc.getMetadata().get("category"))
);
scientificCalc.ifPresent(calc -> {
System.out.println("\n科学计算器: " + calc.getName());
System.out.println("计算 2^3 = " + calc.calculate(2, 3));
});
// 使用自定义选择策略(选择最旧的服务)
Optional<Calculator> oldestCalc = registry.getService(
Calculator.class,
desc -> true,
instances -> instances.stream()
.max(Comparator.comparingLong(ServiceInstance::getAge))
.orElse(null)
);
oldestCalc.ifPresent(calc ->
System.out.println("\n最旧的计算器: " + calc.getName()));
// 获取所有服务
System.out.println("\n所有可用计算器:");
registry.getAllServices(Calculator.class).forEach(calc ->
System.out.println(" - " + calc.getName()));
// 动态服务发现演示
System.out.println("\n=== 动态服务发现 ===");
registry.discoverServices(Calculator.class);
// 清理空闲服务
System.out.println("\n=== 服务清理 ===");
registry.cleanup(60000); // 清理空闲超过60秒的服务
}
}
四、模块化应用打包与部署
4.1 JLink:创建自定义运行时映像
java
import java.nio.file.*;
import java.util.*;
import java.util.spi.ToolProvider;
// JLink工具封装
public class JLinkPackager {
// JLink配置
static class JLinkConfig {
String modulePath;
List<String> modules;
String outputDir;
String launcherName;
Map<String, String> launcherArgs;
boolean compress = true;
boolean stripDebug = true;
boolean includeHeaderFiles = false;
List<String> additionalOptions = new ArrayList<>();
JLinkConfig(String modulePath, List<String> modules, String outputDir) {
this.modulePath = modulePath;
this.modules = modules;
this.outputDir = outputDir;
}
// 生成JLink命令参数
List<String> buildArguments() {
List<String> args = new ArrayList<>();
// 基本参数
args.add("--module-path");
args.add(modulePath);
args.add("--add-modules");
args.add(String.join(",", modules));
// 输出目录
args.add("--output");
args.add(outputDir);
// 可选参数
if (compress) {
args.add("--compress=2"); // 0=无, 1=常量字符串, 2=ZIP
}
if (stripDebug) {
args.add("--strip-debug");
}
if (!includeHeaderFiles) {
args.add("--no-header-files");
args.add("--no-man-pages");
}
// 启动器配置
if (launcherName != null && !modules.isEmpty()) {
args.add("--launcher");
String launcherCmd = launcherName + "=" + modules.get(0);
if (launcherArgs != null && !launcherArgs.isEmpty()) {
launcherCmd += launcherArgs.entrySet().stream()
.map(e -> e.getKey() + "=" + e.getValue())
.reduce("", (a, b) -> a.isEmpty() ? b : a + " " + b);
}
args.add(launcherCmd);
}
// 额外选项
args.addAll(additionalOptions);
return args;
}
}
// 自定义映像构建器
public static class CustomRuntimeBuilder {
private final JLinkConfig config;
private final List<Path> moduleJars = new ArrayList<>();
public CustomRuntimeBuilder(String outputDir) {
this.config = new JLinkConfig("", new ArrayList<>(), outputDir);
}
// 添加模块JAR
public CustomRuntimeBuilder addModule(Path moduleJar) {
moduleJars.add(moduleJar);
return this;
}
// 添加模块目录
public CustomRuntimeBuilder addModuleDirectory(Path moduleDir) {
try {
Files.list(moduleDir)
.filter(p -> p.toString().endsWith(".jar"))
.forEach(moduleJars::add);
} catch (Exception e) {
throw new RuntimeException("无法读取模块目录: " + moduleDir, e);
}
return this;
}
// 设置模块路径
public CustomRuntimeBuilder setModulePath(String modulePath) {
config.modulePath = modulePath;
return this;
}
// 添加模块
public CustomRuntimeBuilder addModule(String moduleName) {
config.modules.add(moduleName);
return this;
}
// 设置启动器
public CustomRuntimeBuilder withLauncher(String name,
Map<String, String> args) {
config.launcherName = name;
config.launcherArgs = args;
return this;
}
// 构建运行时映像
public Path build() throws Exception {
// 如果未显式设置模块路径,则自动构建
if (config.modulePath.isEmpty() && !moduleJars.isEmpty()) {
config.modulePath = buildModulePath();
}
// 执行JLink
ToolProvider jlink = ToolProvider.findFirst("jlink")
.orElseThrow(() -> new RuntimeException("JLink工具不可用"));
List<String> args = config.buildArguments();
System.out.println("执行JLink命令:");
System.out.println("jlink " + String.join(" ", args));
// 创建输出目录
Path outputPath = Paths.get(config.outputDir);
if (Files.exists(outputPath)) {
deleteDirectory(outputPath);
}
// 运行JLink
int exitCode = jlink.run(System.out, System.err,
args.toArray(new String[0]));
if (exitCode != 0) {
throw new RuntimeException("JLink执行失败,退出码: " + exitCode);
}
// 验证生成的运行时
validateRuntime(outputPath);
return outputPath;
}
private String buildModulePath() {
return moduleJars.stream()
.map(Path::toString)
.collect(Collectors.joining(File.pathSeparator));
}
private void validateRuntime(Path runtimeDir) throws Exception {
System.out.println("\n验证运行时映像:");
// 检查必要的目录
Path binDir = runtimeDir.resolve("bin");
Path libDir = runtimeDir.resolve("lib");
if (!Files.exists(binDir) || !Files.exists(libDir)) {
throw new RuntimeException("运行时映像结构不正确");
}
System.out.println("✓ 目录结构验证通过");
// 检查可执行文件
if (config.launcherName != null) {
Path launcher = getLauncherPath(binDir, config.launcherName);
if (Files.exists(launcher) && Files.isExecutable(launcher)) {
System.out.println("✓ 启动器文件创建成功: " + launcher);
}
}
// 检查模块库
try (DirectoryStream<Path> stream =
Files.newDirectoryStream(libDir, "*.so")) {
if (stream.iterator().hasNext()) {
System.out.println("✓ 本地库文件存在");
}
}
// 计算映像大小
long size = calculateDirectorySize(runtimeDir);
System.out.printf("✓ 运行时映像大小: %.2f MB%n", size / 1024.0 / 1024.0);
}
private Path getLauncherPath(Path binDir, String launcherName) {
String osName = System.getProperty("os.name").toLowerCase();
String extension = osName.contains("win") ? ".exe" : "";
return binDir.resolve(launcherName + extension);
}
private long calculateDirectorySize(Path dir) throws Exception {
try (var stream = Files.walk(dir)) {
return stream.filter(Files::isRegularFile)
.mapToLong(p -> {
try { return Files.size(p); }
catch (Exception e) { return 0L; }
})
.sum();
}
}
private void deleteDirectory(Path dir) throws Exception {
if (Files.exists(dir)) {
try (var stream = Files.walk(dir)) {
stream.sorted(Comparator.reverseOrder())
.forEach(p -> {
try { Files.delete(p); }
catch (Exception e) { /* 忽略 */ }
});
}
}
}
}
// 模块依赖分析器
public static class ModuleDependencyAnalyzer {
public static Set<String> analyzeDependencies(String mainModule,
String modulePath) {
Set<String> allModules = new LinkedHashSet<>();
allModules.add(mainModule);
// 递归收集传递依赖
collectDependencies(mainModule, modulePath, allModules);
return allModules;
}
private static void collectDependencies(String moduleName,
String modulePath,
Set<String> collected) {
try {
// 使用java命令分析模块
ProcessBuilder pb = new ProcessBuilder(
"java",
"--module-path", modulePath,
"--module", "java.base/jdk.internal.module.ModuleAnalyzer",
moduleName
);
Process process = pb.start();
String output = new String(process.getInputStream().readAllBytes());
// 解析输出,获取依赖模块
// 这里简化处理,实际需要解析详细的模块依赖信息
String[] lines = output.split("\n");
for (String line : lines) {
if (line.contains("requires") && !line.contains("java.base")) {
String depModule = extractModuleName(line);
if (depModule != null && !collected.contains(depModule)) {
collected.add(depModule);
collectDependencies(depModule, modulePath, collected);
}
}
}
} catch (Exception e) {
System.err.println("模块依赖分析失败: " + e.getMessage());
}
}
private static String extractModuleName(String line) {
// 简单提取模块名,实际需要更复杂的解析
String[] parts = line.trim().split("\s+");
for (String part : parts) {
if (!part.isEmpty() && !part.equals("requires") &&
!part.equals("transitive") && !part.equals("static")) {
return part;
}
}
return null;
}
}
public static void main(String[] args) throws Exception {
System.out.println("=== JLink自定义运行时映像构建 ===\n");
// 示例:构建一个简单的控制台应用运行时
// 1. 准备模块
Path modulesDir = Paths.get("modules");
if (!Files.exists(modulesDir)) {
Files.createDirectories(modulesDir);
// 这里应该放置编译好的模块JAR文件
// 例如:com.example.app.jar, com.example.utils.jar 等
}
// 2. 配置JLink
CustomRuntimeBuilder builder = new CustomRuntimeBuilder("myruntime");
builder.addModuleDirectory(modulesDir)
.addModule("com.example.app") // 主模块
.withLauncher("myapp", Map.of("main-class",
"com.example.app.Main"));
// 3. 分析依赖(可选)
Set<String> dependencies = ModuleDependencyAnalyzer.analyzeDependencies(
"com.example.app",
modulesDir.toString()
);
System.out.println("模块依赖分析结果:");
dependencies.forEach(mod -> System.out.println(" - " + mod));
// 4. 构建运行时
Path runtime = builder.build();
System.out.println("\n=== 运行时映像构建完成 ===");
System.out.println("位置: " + runtime.toAbsolutePath());
// 5. 创建启动脚本
createLaunchScripts(runtime);
// 6. 测试运行时
testRuntime(runtime);
}
private static void createLaunchScripts(Path runtimeDir) throws Exception {
Path binDir = runtimeDir.resolve("bin");
// 创建Windows批处理脚本
Path windowsScript = binDir.resolve("launch.bat");
String windowsContent = "@echo off\n" +
"setlocal\n" +
"set JAVA_HOME=%~dp0..\n" +
"set PATH=%%JAVA_HOME%%\bin;%%PATH%%\n" +
"java --module-path ..\lib --module com.example.app/com.example.app.Main %%*\n";
Files.writeString(windowsScript, windowsContent);
// 创建Unix/Linux Shell脚本
Path unixScript = binDir.resolve("launch.sh");
String unixContent = "#!/bin/bash\n" +
"DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"\n" +
"export JAVA_HOME="$DIR/.."\n" +
"export PATH="$JAVA_HOME/bin:$PATH"\n" +
"java --module-path ../lib --module com.example.app/com.example.app.Main "$@"\n";
Files.writeString(unixScript, unixContent);
Files.setPosixFilePermissions(unixScript,
Set.of(PosixFilePermission.OWNER_EXECUTE,
PosixFilePermission.OWNER_READ,
PosixFilePermission.OWNER_WRITE));
System.out.println("\n启动脚本已创建:");
System.out.println(" Windows: launch.bat");
System.out.println(" Unix/Linux: launch.sh");
}
private static void testRuntime(Path runtimeDir) throws Exception {
System.out.println("\n=== 测试运行时映像 ===");
Path javaExecutable;
String osName = System.getProperty("os.name").toLowerCase();
if (osName.contains("win")) {
javaExecutable = runtimeDir.resolve("bin").resolve("java.exe");
} else {
javaExecutable = runtimeDir.resolve("bin").resolve("java");
}
if (!Files.exists(javaExecutable)) {
System.err.println("Java可执行文件不存在: " + javaExecutable);
return;
}
// 测试Java版本
ProcessBuilder pb = new ProcessBuilder(
javaExecutable.toString(),
"--version"
);
Process process = pb.start();
String output = new String(process.getInputStream().readAllBytes());
System.out.println("Java版本信息:");
System.out.println(output);
// 测试模块列表
pb = new ProcessBuilder(
javaExecutable.toString(),
"--list-modules"
);
process = pb.start();
output = new String(process.getInputStream().readAllBytes());
System.out.println("包含的模块:");
System.out.println(output);
System.out.println("✓ 运行时映像测试通过");
}
}
五、模块化迁移策略与最佳实践
5.1 从传统应用到模块化应用的迁移
java
import java.nio.file.*;
import java.util.*;
// 模块化迁移助手
public class ModularMigrationHelper {
// 迁移步骤跟踪器
static class MigrationTracker {
private final Path projectRoot;
private final Map<String, MigrationStatus> modules = new HashMap<>();
private final List<String> migrationSteps = Arrays.asList(
"分析现有依赖",
"创建模块描述符",
"解决依赖冲突",
"重构包结构",
"测试模块隔离",
"构建模块化JAR",
"创建运行时映像"
);
enum MigrationStatus {
NOT_STARTED,
ANALYZING,
IN_PROGRESS,
COMPLETED,
BLOCKED
}
MigrationTracker(Path projectRoot) {
this.projectRoot = projectRoot;
}
// 分析项目结构
public ProjectAnalysis analyze() throws Exception {
ProjectAnalysis analysis = new ProjectAnalysis();
// 扫描项目目录
try (var stream = Files.walk(projectRoot)) {
stream.filter(Files::isRegularFile)
.filter(p -> p.toString().endsWith(".java"))
.forEach(p -> analysis.addJavaFile(p));
stream.filter(Files::isRegularFile)
.filter(p -> p.toString().endsWith(".jar"))
.forEach(p -> analysis.addJarFile(p));
}
// 分析依赖
analysis.analyzeDependencies();
return analysis;
}
// 生成迁移计划
public MigrationPlan generatePlan(ProjectAnalysis analysis) {
MigrationPlan plan = new MigrationPlan();
// 识别潜在的模块
Set<String> packageRoots = analysis.getPackageRoots();
for (String pkgRoot : packageRoots) {
MigrationModule module = new MigrationModule(pkgRoot);
// 分析模块依赖
Set<String> dependencies = analysis.getDependenciesForPackage(pkgRoot);
module.setDependencies(dependencies);
// 确定迁移优先级
int priority = calculatePriority(pkgRoot, dependencies);
module.setPriority(priority);
plan.addModule(module);
}
// 排序模块(从依赖少的开始)
plan.sortModules();
return plan;
}
private int calculatePriority(String pkgRoot, Set<String> dependencies) {
// 依赖越少,优先级越高(越容易迁移)
int dependencyCount = dependencies.size();
// 核心包优先级更高
if (pkgRoot.startsWith("com.example.core")) {
return 1;
}
return Math.max(2, 10 - dependencyCount);
}
}
static class ProjectAnalysis {
private final List<Path> javaFiles = new ArrayList<>();
private final List<Path> jarFiles = new ArrayList<>();
private final Map<String, Set<String>> packageDependencies = new HashMap<>();
void addJavaFile(Path file) { javaFiles.add(file); }
void addJarFile(Path file) { jarFiles.add(file); }
void analyzeDependencies() throws Exception {
// 简化的依赖分析
for (Path javaFile : javaFiles) {
String content = Files.readString(javaFile);
String packageName = extractPackageName(content);
if (packageName != null) {
Set<String> imports = extractImports(content);
packageDependencies.put(packageName, imports);
}
}
}
Set<String> getPackageRoots() {
Set<String> roots = new HashSet<>();
for (String pkg : packageDependencies.keySet()) {
String root = pkg.split("\.")[0]; // 简单的根包提取
roots.add(root);
}
return roots;
}
Set<String> getDependenciesForPackage(String pkgRoot) {
Set<String> deps = new HashSet<>();
for (Map.Entry<String, Set<String>> entry : packageDependencies.entrySet()) {
if (entry.getKey().startsWith(pkgRoot)) {
deps.addAll(entry.getValue());
}
}
return deps;
}
private String extractPackageName(String content) {
// 简单的包名提取
int packageIndex = content.indexOf("package ");
if (packageIndex >= 0) {
int endIndex = content.indexOf(";", packageIndex);
if (endIndex >= 0) {
return content.substring(packageIndex + 8, endIndex).trim();
}
}
return null;
}
private Set<String> extractImports(String content) {
Set<String> imports = new HashSet<>();
String[] lines = content.split("\n");
for (String line : lines) {
line = line.trim();
if (line.startsWith("import ")) {
String importStmt = line.substring(7, line.indexOf(";"));
imports.add(importStmt);
}
}
return imports;
}
}
static class MigrationPlan {
private final List<MigrationModule> modules = new ArrayList<>();
void addModule(MigrationModule module) {
modules.add(module);
}
void sortModules() {
modules.sort(Comparator.comparingInt(MigrationModule::getPriority));
}
public List<MigrationModule> getModules() {
return Collections.unmodifiableList(modules);
}
}
static class MigrationModule {
private final String name;
private Set<String> dependencies = new HashSet<>();
private int priority;
private String moduleInfoTemplate;
MigrationModule(String name) {
this.name = name;
}
void setDependencies(Set<String> dependencies) {
this.dependencies = dependencies;
}
void setPriority(int priority) {
this.priority = priority;
}
String getName() { return name; }
Set<String> getDependencies() { return dependencies; }
int getPriority() { return priority; }
// 生成module-info.java模板
String generateModuleInfo() {
StringBuilder sb = new StringBuilder();
sb.append("module ").append(name).append(" {\n");
// 添加依赖
for (String dep : dependencies) {
// 简化处理:假设所有依赖都是模块
sb.append(" requires ").append(extractModuleName(dep)).append(";\n");
}
// 自动导出包(根据实际包结构调整)
sb.append("\n");
sb.append(" exports ").append(name).append(";\n");
sb.append("}\n");
return sb.toString();
}
private String extractModuleName(String importStmt) {
// 简化处理:从import语句提取模块名
String[] parts = importStmt.split("\.");
if (parts.length > 1) {
return parts[0] + "." + parts[1];
}
return importStmt;
}
}
// 自动迁移工具
public static class AutoMigrator {
public static void migrateProject(Path projectDir) throws Exception {
System.out.println("开始迁移项目: " + projectDir);
MigrationTracker tracker = new MigrationTracker(projectDir);
// 1. 分析现有项目
System.out.println("\n1. 分析项目结构...");
ProjectAnalysis analysis = tracker.analyze();
System.out.println(" 找到 " + analysis.javaFiles.size() + " 个Java文件");
System.out.println(" 找到 " + analysis.jarFiles.size() + " 个JAR文件");
// 2. 生成迁移计划
System.out.println("\n2. 生成迁移计划...");
MigrationPlan plan = tracker.generatePlan(analysis);
System.out.println(" 识别出 " + plan.getModules().size() + " 个潜在模块:");
for (MigrationModule module : plan.getModules()) {
System.out.printf(" - %s (优先级: %d, 依赖: %d)%n",
module.getName(), module.getPriority(),
module.getDependencies().size());
}
// 3. 逐步迁移
System.out.println("\n3. 开始迁移...");
for (MigrationModule module : plan.getModules()) {
migrateModule(projectDir, module);
}
System.out.println("\n迁移完成!");
System.out.println("\n后续步骤:");
System.out.println("1. 审查生成的module-info.java文件");
System.out.println("2. 运行测试确保功能正常");
System.out.println("3. 使用JLink创建自定义运行时");
System.out.println("4. 优化模块边界和依赖");
}
private static void migrateModule(Path projectDir, MigrationModule module)
throws Exception {
System.out.println("\n迁移模块: " + module.getName());
// 创建模块目录
Path moduleDir = projectDir.resolve(module.getName());
Files.createDirectories(moduleDir);
// 生成module-info.java
Path moduleInfo = moduleDir.resolve("module-info.java");
String content = module.generateModuleInfo();
Files.writeString(moduleInfo, content);
System.out.println(" 创建 module-info.java:");
System.out.println(" ```");
System.out.println(content);
System.out.println(" ```");
// 移动源文件(简化示例)
// 实际中需要根据包结构移动文件
System.out.println(" 提示:需要将包 " + module.getName() +
" 下的源文件移动到模块目录中");
}
}
// 兼容性包装器(用于混合模块/非模块环境)
public static class CompatibilityLayer {
// 自动模块检测器
public static boolean isAutomaticModule(Path jarFile) {
try {
// 检查JAR是否包含module-info.class
FileSystem fs = FileSystems.newFileSystem(jarFile,
(ClassLoader) null);
Path moduleInfo = fs.getPath("/", "module-info.class");
boolean isModule = Files.exists(moduleInfo);
fs.close();
return !isModule; // 没有module-info.class的是自动模块
} catch (Exception e) {
// 读取失败,假设是自动模块
return true;
}
}
// 为传统JAR生成自动模块名称
public static String generateAutomaticModuleName(Path jarFile) {
String fileName = jarFile.getFileName().toString();
// 移除.jar扩展名
String baseName = fileName.substring(0, fileName.length() - 4);
// 处理版本号(如myapp-1.2.3.jar -> myapp)
baseName = baseName.replaceAll("-\d+(\.\d+)*", "");
// 转换为有效的模块名(替换非法字符)
baseName = baseName.replaceAll("[^A-Za-z0-9.]", ".");
baseName = baseName.replaceAll("\.+", ".");
baseName = baseName.replaceAll("^\.|\.$", "");
// 确保至少有一个点
if (!baseName.contains(".")) {
baseName = "auto." + baseName;
}
return baseName;
}
// 创建桥接模块(连接模块化和非模块化代码)
public static String createBridgeModule(String moduleName,
Set<String> automaticModules) {
StringBuilder sb = new StringBuilder();
sb.append("module ").append(moduleName).append(" {\n");
sb.append(" // 桥接模块:连接模块化和传统代码\n");
sb.append(" requires java.base;\n");
sb.append("\n");
for (String autoModule : automaticModules) {
sb.append(" requires ").append(autoModule).append(";\n");
}
sb.append("\n");
sb.append(" // 重新导出所有包\n");
sb.append(" exports * to all;\n");
sb.append(" opens * to all;\n");
sb.append("}\n");
return sb.toString();
}
}
public static void main(String[] args) throws Exception {
System.out.println("=== Java模块化迁移助手 ===\n");
if (args.length == 0) {
System.out.println("用法: java ModularMigrationHelper <项目目录>");
System.out.println("示例: java ModularMigrationHelper ./myproject");
return;
}
Path projectDir = Paths.get(args[0]);
if (!Files.exists(projectDir)) {
System.err.println("目录不存在: " + projectDir);
return;
}
// 演示自动迁移
AutoMigrator.migrateProject(projectDir);
// 演示兼容性处理
System.out.println("\n=== 兼容性处理演示 ===");
// 扫描项目中的JAR文件
try (var stream = Files.walk(projectDir)) {
List<Path> jarFiles = stream
.filter(p -> p.toString().endsWith(".jar"))
.collect(Collectors.toList());
System.out.println("\n分析传统JAR文件:");
for (Path jarFile : jarFiles) {
boolean isAutoModule = CompatibilityLayer.isAutomaticModule(jarFile);
String moduleName = CompatibilityLayer.generateAutomaticModuleName(jarFile);
System.out.printf(" %s: %s (自动模块名: %s)%n",
jarFile.getFileName(),
isAutoModule ? "自动模块" : "显式模块",
moduleName);
}
}
System.out.println("\n迁移最佳实践:");
System.out.println("1. 从下到上迁移(依赖少的模块先迁移)");
System.out.println("2. 使用自动模块作为过渡");
System.out.println("3. 逐步替换传统依赖为模块化版本");
System.out.println("4. 使用桥接模块处理复杂的依赖关系");
System.out.println("5. 利用JLink优化部署包大小");
}
}
总结:模块化编程的核心价值
模块化带来的好处:
- 强封装性:明确API边界,隐藏内部实现
- 可靠的配置:编译时检查依赖,避免运行时错误
- 更好的可维护性:模块化架构更清晰,便于理解和维护
- 改进的性能:JLink可以创建更小的运行时映像
- 增强的安全性:通过限制反射访问增强安全性
迁移路径建议:
- 评估阶段:分析现有项目的依赖关系
- 渐进迁移:使用自动模块作为过渡
- 模块化核心:先迁移基础模块和工具模块
- 全面模块化:逐步迁移所有模块
- 优化部署:使用JLink创建最小化运行时
重要工具和命令:
| 工具/命令 | 用途 |
|---|---|
javac --module-source-path |
编译模块 |
java --module-path |
运行模块化应用 |
jlink |
创建自定义运行时映像 |
jmod |
创建JMOD文件 |
jdeps |
分析依赖关系 |