05-Java语言核心-语法特性-模块化系统(JPMS)详解

7 阅读16分钟

模块化系统(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. 渐进式迁移

    阶段1:在类路径上运行(传统方式)
    阶段2:使用自动模块引入依赖
    阶段3:逐步添加module-info.java
    阶段4:完全模块化
    
  2. 合理的模块划分

    // 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;
    }
    
  3. 最小化导出

    module com.example.app {
        // 只导出必要的API包
        exports com.example.api;
        
        // 不要导出:
        // - 实现包
        // - 内部工具包
        // - 模型包(用opens代替)
    }
    
  4. 使用限定导出和开放

    module com.example.app {
        // 只给框架开放
        opens com.example.model to org.hibernate.core;
        
        // 只给测试模块导出
        exports com.example.internal to com.example.test;
    }
    
  5. 服务优先设计

    // 优于直接依赖实现类
    module com.example.client {
        requires com.example.api;  // 只依赖API
        uses com.example.api.Service;  // 使用服务加载
    }
    
  6. 处理第三方库

    module com.example.app {
        // 第三方库作为自动模块
        requires gson;  // 自动模块,JAR名作为模块名
        
        // 或者放在类路径上
    }
    

常见问题

  1. 包分裂问题

    // 错误:同一个包在多个模块中
    module moduleA {
        exports com.example.util;  // 包含StringUtil
    }
    
    module moduleB {
        exports com.example.util;  // 包含DateUtil - 冲突!
    }
    
  2. 循环依赖

    // 错误:循环依赖
    module moduleA {
        requires moduleB;
    }
    
    module moduleB {
        requires moduleA;  // 循环!
    }
    
    // 解决方案:提取公共模块
    module common {
        // 公共部分
    }
    module moduleA {
        requires common;
    }
    module moduleB {
        requires common;
    }
    
  3. 反射访问被拒绝

    // 运行时报错:InaccessibleObjectException
    Field field = clazz.getDeclaredField("privateField");
    field.setAccessible(true);  // 如果没有opens,会抛异常
    
    // 解决:在module-info.java中添加opens
    opens com.example.model to 调用模块;
    
  4. 自动模块的传递依赖

    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工具:创建自定义运行时镜像