当前位置: 首页 > news >正文

Java 9+ 模块化系统(Jigsaw)实战:从 Jar 地狱到模块解耦的架构升级

在 Java 9 之前,Java 应用的依赖管理长期面临 “Jar 地狱”(Jar Hell)问题 —— 依赖冲突、类路径混乱、冗余依赖等问题频发,尤其在大型项目中,仅靠classpath管理依赖往往导致系统架构臃肿、维护成本高。为解决这一痛点,Java 9 正式引入模块化系统(Project Jigsaw),通过 “显式模块定义”“可控的包访问权限”“精确的依赖声明” 三大核心能力,重新定义了 Java 应用的构建与部署方式。本文将从传统 Jar 依赖的痛点出发,详解模块化系统的核心概念、语法规则、实战场景及迁移策略,帮你掌握这一面向大型工程的架构升级技术。

一、为什么需要模块化系统?—— 传统 Jar 依赖的 4 大痛点

在理解 Java 模块化系统之前,我们首先要明确:传统classpath机制在依赖管理、权限控制、架构设计等方面存在明显短板,这些痛点在大型项目或框架开发中尤为突出。

1.1 痛点 1:Jar 地狱(依赖冲突与版本混乱)

传统 Java 应用通过classpath加载类文件,但classpath是一个 “扁平的类容器”,无法区分不同版本的 Jar 包,导致依赖冲突。例如,项目同时依赖spring-core-4.3.0.jar和spring-core-5.3.0.jar,classpath会优先加载路径靠前的 Jar 包,若后续代码依赖高版本的 API,会抛出NoSuchMethodError或ClassNotFoundException。

示例:依赖冲突导致的运行时异常

// 场景:项目同时依赖spring-core-4.3.0.jar(低版本)和spring-core-5.3.0.jar(高版本)

public class SpringDependencyConflictDemo {

public static void main(String[] args) {

// Spring 5.3.0新增的API:GenericApplicationContext.setApplicationStartup()

GenericApplicationContext context = new GenericApplicationContext();

// 若classpath优先加载4.3.0版本,会抛出NoSuchMethodError

context.setApplicationStartup(new DefaultApplicationStartup());

}

}

运行结果(错误):

Exception in thread "main" java.lang.NoSuchMethodError:

org.springframework.context.support.GenericApplicationContext.setApplicationStartup(Lorg/springframework/core/metrics/ApplicationStartup;)V

at SpringDependencyConflictDemo.main(SpringDependencyConflictDemo.java:8)

问题分析:
  • 版本不可控:classpath无法指定依赖版本,只能被动接受 “路径优先” 或 “先到先得” 的加载规则;
  • 冲突难排查:依赖冲突往往在运行时暴露,需通过mvn dependency:tree等工具手动分析依赖树,排查成本高。

1.2 痛点 2:类访问权限过度开放(封装性缺失)

传统 Java 的访问权限(public、protected、default、private)仅作用于类内部或包内,无法限制 “跨 Jar 包的访问”。例如,一个 Jar 包中的public类会被所有依赖该 Jar 的项目访问,即使该类仅为内部工具类,无需对外暴露,导致封装性缺失,增加 API 维护成本。

示例:过度开放的内部工具类

// Jar包:common-utils-1.0.jar中的内部工具类(本应仅内部使用)

package com.example.common.utils;

// 为方便内部调用,误定义为public

public class InternalStringUtils {

// 内部工具方法:仅用于common-utils.jar内部字符串处理

public static String formatInternal(String input) {

return input.replaceAll("\\s+", "");

}

}

// 其他项目依赖common-utils-1.0.jar,意外调用内部方法

public class ExternalProjectDemo {

public static void main(String[] args) {

// 外部项目调用了本应内部使用的方法

String result = InternalStringUtils.formatInternal("hello world");

System.out.println(result); // 输出:helloworld

}

}

问题分析:
  • 封装性破坏:public修饰符导致内部工具类被外部项目访问,违反 “最小权限原则”;
  • API 兼容性风险:若后续InternalStringUtils修改或删除formatInternal()方法,会导致依赖该方法的外部项目崩溃。

1.3 痛点 3:冗余依赖与资源浪费

传统classpath机制下,项目往往依赖 “过度完整” 的 Jar 包(如为使用spring-core的某个工具类,需引入整个spring-contextJar 包),导致最终部署包体积庞大,冗余依赖占用磁盘空间和内存资源。

示例:冗余依赖的 Spring 项目

<!-- 传统Maven依赖:为使用Spring的BeanUtils,引入整个spring-context -->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-context</artifactId>

<version>5.3.0</version>

</dependency>

问题分析:
  • 依赖膨胀:spring-context依赖spring-core、spring-beans、spring-aop等多个 Jar 包,总大小超过 1MB,而实际仅使用BeanUtils.copyProperties()一个方法;
  • 部署效率低:冗余依赖导致部署包体积增大,传输和启动时间变长,尤其在容器化部署(如 Docker)场景中,影响迭代效率。

1.4 痛点 4:模块化架构设计困难

传统 Java 项目缺乏 “模块化” 的语言级支持,只能通过包结构(如com.example.service、com.example.dao)模拟模块划分,但这种划分仅停留在代码组织层面,无法在编译期和运行期强制模块边界,导致模块间依赖混乱(如 DAO 层直接依赖 Controller 层)。

示例:模块边界混乱的传统项目

// 包结构模拟模块划分,但无强制边界

com.example

├── controller // 控制层(应依赖service层,不应被service依赖)

│ └── UserController.java

├── service // 服务层(应依赖dao层,不应依赖controller层)

│ └── UserService.java

└── dao // 数据访问层(不应依赖其他层)

└── UserDao.java

// 错误依赖:service层依赖controller层(违反分层架构)

package com.example.service;

import com.example.controller.UserController; // 错误:service依赖controller

public class UserService {

public void processUser() {

UserController controller = new UserController(); // 直接实例化controller

controller.getUserInfo();

}

}

问题分析:
  • 架构边界模糊:包结构无法阻止跨层依赖,导致架构设计沦为 “纸面规则”;
  • 维护成本高:模块间耦合度高,修改一个模块可能影响多个依赖模块,代码重构困难。

1.2 Java 模块化系统的核心价值

Java 9 + 模块化系统通过以下 4 点设计,从根本上解决了传统 Jar 依赖的痛点:

  1. 显式模块定义:通过module-info.java文件明确模块名称、依赖模块及导出包,实现 “模块级” 的依赖管理;
  1. 可控的访问权限:仅导出模块中需要对外暴露的包,内部包默认隐藏,强化封装性;
  1. 精确依赖声明:模块需显式声明依赖的其他模块,避免隐式依赖和版本冲突;
  1. 架构边界强制:编译期和运行期强制模块间的依赖规则,防止跨模块的非法访问,保障架构设计落地。

二、Java 模块化系统的核心概念与语法规则

Java 模块化系统的核心是 “模块(Module)”—— 一个包含代码和资源的独立单元,通过module-info.java文件定义模块的元信息。掌握模块的定义、依赖、导出等语法规则,是使用模块化系统的基础。

2.1 1. 核心概念:模块、模块描述文件与模块路径

(1)模块(Module)

模块是 Java 模块化系统的基本单元,具有以下特性:

  • 唯一性:每个模块有唯一的模块名称(如java.base、com.example.service);
  • 自包含性:模块包含 Java 类、资源文件(如配置文件)及module-info.java描述文件;
  • 依赖明确性:模块需显式声明依赖的其他模块,无隐式依赖;
  • 访问可控性:模块仅导出指定的包,未导出的包仅内部可见。
(2)模块描述文件(module-info.java)

module-info.java是模块的 “身份证”,位于模块的根目录(如src/main/java/module-info.java),用于定义模块的元信息,包括模块名称、依赖模块、导出包等。

(3)模块路径(Module Path)

模块路径是 Java 9 + 新增的类加载路径,用于替代传统的classpath,支持加载模块化的 Jar 包(Module Jar)或未模块化的 Jar 包(Automatic Module)。模块路径的加载优先级高于classpath。

2.2 2. 模块描述文件的核心语法

module-info.java支持 6 类核心语法,覆盖模块定义、依赖、导出、服务等场景:

(1)定义模块名称(module关键字)

// 定义模块:模块名称为com.example.service(遵循反向域名规则,确保唯一性)

module com.example.service {

// 模块元信息(依赖、导出等)

}

(2)声明依赖模块(requires关键字)
  • 普通依赖:requires 模块名;,表示当前模块依赖指定模块,且依赖是传递的(若 A 依赖 B,B 依赖 C,则 A 间接依赖 C);
  • 非传递依赖:requires static 模块名;,表示依赖仅在编译期有效,运行期可选(如依赖编译工具类);
  • 强制依赖:requires transitive 模块名;,表示当前模块的依赖会传递给依赖当前模块的其他模块(如 A 依赖 B,且 B requires transitive C,则 A 自动依赖 C)。
示例:声明模块依赖

module com.example.service {

// 普通依赖:依赖com.example.dao模块(运行期必需)

requires com.example.dao;

// 非传递依赖:依赖com.example.utils模块(仅编译期需要)

requires static com.example.utils;

// 强制依赖:依赖com.example.model模块,且传递给依赖当前模块的模块

requires transitive com.example.model;

}

(3)导出包(exports关键字)

exports 包名;表示当前模块导出指定的包,允许其他模块访问该包中的public类和接口;未导出的包仅当前模块内部可见,强化封装性。

示例:导出模块的公共包

module com.example.service {

requires com.example.dao;

// 导出公共包:允许其他模块访问com.example.service.api包中的类

exports com.example.service.api;

// 不导出内部包:com.example.service.internal仅模块内部可见

// (无需显式声明,默认不导出)

}

(4)定向导出包(exports...to关键字)

exports 包名 to 模块名1, 模块名2;表示当前模块仅向指定模块导出包,其他模块无法访问,适用于 “模块间协作但不对外暴露” 的场景。

示例:定向导出包

module com.example.service {

requires com.example.dao;

// 仅向com.example.controller模块导出com.example.service.api包

exports com.example.service.api to com.example.controller;

// 其他模块(如com.example.client)无法访问com.example.service.api

}

(5)提供服务(provides...with关键字)

provides 服务接口名 with 服务实现类名;表示当前模块提供指定服务接口的实现,遵循 “服务发现” 机制,支持模块解耦(如 A 模块依赖服务接口,B 模块提供实现,A 无需依赖 B)。

(6)使用服务(uses关键字)

uses 服务接口名;表示当前模块使用指定的服务接口,通过ServiceLoader加载服务实现类,无需显式依赖服务实现模块。

示例:服务提供与使用

// 1. 服务接口模块(com.example.service.api)

module com.example.service.api {

// 导出服务接口包

exports com.example.service.api;

}

// 服务接口(com.example.service.api.UserService)

package com.example.service.api;

public interface UserService {

String getUserInfo(Long userId);

}

// 2. 服务实现模块(com.example.service.impl)

module com.example.service.impl {

// 依赖服务接口模块

requires com.example.service.api;

// 提供服务实现:UserService接口由UserServiceImpl类实现

provides com.example.service.api.UserService with com.example.service.impl.UserServiceImpl;

}

// 服务实现类(com.example.service.impl.UserServiceImpl)

package com.example.service.impl;

import com.example.service.api.UserService;

public class UserServiceImpl implements UserService {

@Override

public String getUserInfo(Long userId) {

return "用户ID:" + userId + ",姓名:张三";

}

}

// 3. 服务使用模块(com.example.controller)

module com.example.controller {

// 依赖服务接口模块

requires com.example.service.api;

// 声明使用UserService服务接口

uses com.example.service.api.UserService;

// 导出控制器包

exports com.example.controller;

}

// 服务使用类(com.example.controller.UserController)

package com.example.controller;

import com.example.service.api.UserService;

import java.util.ServiceLoader;

public class UserController {

public String getUserInfo(Long userId) {

// 通过ServiceLoader加载服务实现(无需依赖com.example.service.impl模块)

ServiceLoader<UserService> serviceLoader = ServiceLoader.load(UserService.class);

UserService userService = serviceLoader.findFirst().orElseThrow();

return userService.getUserInfo(userId);

}

}

2.3 3. 模块化 Jar 包与自动模块

Java 9 + 支持两种类型的 Jar 包:

(1)模块化 Jar 包(Module Jar)

包含module-info.class(由module-info.java编译生成)的 Jar 包,是标准的模块化 Jar,支持显式依赖和导出控制。

(2)自动模块(Automatic Module)

未包含module-info.class的传统 Jar 包,Java 会自动将其转换为 “自动模块”,模块名称由 Jar 包名推导(如spring-core-5.3.0.jar的自动模块名为spring.core)。自动模块的所有包默认导出,且依赖所有其他模块。

示例:使用自动模块(依赖传统 Spring Jar 包)

// 模块描述文件:依赖传统Spring Jar包(自动模块)

module com.example.client {

// 依赖Spring的自动模块(模块名由Jar包名推导:spring.context)

requires spring.context;

// 导出客户端包

exports com.example.client;

}

注意事项:
  • 自动模块兼容性:自动模块是为了兼容传统 Jar 包,建议优先使用模块化 Jar 包;
  • 模块名推导规则:Jar 包名中的非字母数字字符(如-、_)会被替换为.,版本号会被忽略(如spring-core-5.3.0.jar→spring.core)。

三、Java 模块化系统的实战场景:从项目构建到架构解耦

Java 模块化系统在实际开发中应用广泛,本节将结合 3 个典型场景(模块化项目构建、模块间服务协作、传统项目模块化迁移),展示从需求分析到模块化实现的完整过程。

3.1 场景 1:模块化项目构建(Maven+Java 17)

需求:构建一个模块化的电商项目,包含 4 个模块:

  1. com.example.model:实体类模块(如User、Order);
  1. com.example.dao:数据访问模块(依赖model模块);
  1. com.example.service:服务模块(依赖dao模块,导出服务接口);
  1. com.example.controller:控制层模块(依赖service模块,使用服务接口)。
实现步骤:
(1)项目结构设计

e-commerce-modular/

├── pom.xml // 父Maven pom(管理子模块)

├── model/ // com.example.model模块

│ ├── pom.xml

│ └── src/main/java/

│ ├── module-info.java

│ └── com/example/model/

│ ├── User.java

│ └── Order.java

├── dao/ // com.example.dao模块

│ ├── pom.xml

│ └── src/main/java/

│ ├── module-info.java

│ └── com/example/dao/

│ ├── UserDao.java

│ └── OrderDao.java

├── service/ // com.example.service模块

│ ├── pom.xml

│ └── src/main/java/

│ ├── module-info.java

│ └── com/example/service/

│ ├── api/ // 服务接口包(导出)

│ │ ├── UserService.java

│ │ └── OrderService.java

│ └── impl/ // 服务实现包(不导出)

│ ├── UserServiceImpl.java

│ └── OrderServiceImpl.java

└── controller/ // com.example.controller模块

├── pom.xml

└── src/main/java/

├── module-info.java

└── com/example/controller/

├── UserController.java

└── OrderController.java

(2)父模块 pom.xml(管理子模块)

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>com.example</groupId>

<artifactId>e-commerce-modular</artifactId>

<version>1.0.0</version>

<packaging>pom</packaging>

<name>e-commerce-modular</name>

<description>Java模块化电商项目</description>

<!-- 子模块 -->

<modules>

<module>model</module>

<module>dao</module>

<module>service</module>

<module>controller</module>

</modules>

<!-- 统一配置 -->

<properties>

<maven.compiler.source>17</maven.compiler.source>

<maven.compiler.target>17</maven.compiler.target>

<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

</properties>

</project>

(3)各子模块实现
① com.example.model模块
  • module-info.java

module com.example.model {

// 导出实体类包,允许其他模块访问

exports com.example.model;

}

  • User.java

package com.example.model;

public class User {

private Long id;

private String username;

private String email;

// 构造器、getter、setter

}

  • pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<parent>

<groupId>com.example</groupId>

<artifactId>e-commerce-modular</artifactId>

<version>1.0.0</version>

</parent>

<modelVersion>4.0.0</modelVersion>

<artifactId>model</artifactId>

<name>model</name>

</project>

② com.example.dao模块
  • module-info.java

module com.example.dao {

// 依赖model模块

requires com.example.model;

// 导出DAO接口包(服务实现包不导出)

exports com.example.dao.api;

}

  • UserDao.java(接口,com.example.dao.api包):

package com.example.dao.api;

import com.example.model.User;

public interface UserDao {

User getUserById(Long id);

}

  • UserDaoImpl.java(实现,com.example.dao.impl包,不导出):

package com.example.dao.impl;

import com.example.dao.api.UserDao;

import com.example.model.User;

public class UserDaoImpl implements UserDao {

@Override

public User getUserById(Long id) {

// 模拟数据库查询

User user = new User();

user.setId(id);

user.setUsername("张三");

user.setEmail("zhangsan@example.com");

return user;

}

}

  • pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<parent>

<groupId>com.example</groupId>

<artifactId>e-commerce-modular</artifactId>

<version>1.0.0</version>

</parent>

<modelVersion>4.0.0</modelVersion>

<artifactId>dao</artifactId>

<name>dao</name>

<!-- 依赖model模块 -->

<dependencies>

<dependency>

<groupId>com.example</groupId>

<artifactId>model</artifactId>

<version>1.0.0</version>

</dependency>

</dependencies>

</project>

③ com.example.service模块
  • module-info.java

module com.example.service {

// 依赖dao模块(非传递,仅当前模块依赖)

requires com.example.dao;

// 依赖model模块(传递依赖,让依赖当前模块的模块自动依赖model)

requires transitive com.example.model;

// 导出服务接口包

exports com.example.service.api;

// 提供服务实现(服务接口由实现类提供)

provides com.example.service.api.UserService with com.example.service.impl.UserServiceImpl;

}

  • UserService.java(接口,com.example.service.api包):

package com.example.service.api;

import com.example.model.User;

public interface UserService {

User getUserInfo(Long userId);

}

  • UserServiceImpl.java(实现,com.example.service.impl包):

package com.example.service.impl;

import com.example.dao.api.UserDao;

import com.example.dao.impl.UserDaoImpl;

import com.example.model.User;

import com.example.service.api.UserService;

public class UserServiceImpl implements UserService {

private final UserDao userDao = new UserDaoImpl();

@Override

public User getUserInfo(Long userId) {

return userDao.getUserById(userId);

}

}

  • pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<parent>

<groupId>com.example</groupId>

<artifactId>e-commerce-modular</artifactId>

<version>1.0.0</version>

</parent>

<modelVersion>4.0.0</modelVersion>

<artifactId>service</artifactId>

<name>service</name>

<dependencies>

<dependency>

<groupId>com.example</groupId>

<artifactId>dao</artifactId>

<version>1.0.0</version>

</dependency>

<dependency>

<groupId>com.example</groupId>

<artifactId>model</artifactId>

<version>1.0.0</version>

</dependency>

</dependencies>

</project>

④ com.example.controller模块
  • module-info.java

module com.example.controller {

// 依赖service模块(获取服务接口)

requires com.example.service;

// 使用UserService服务

uses com.example.service.api.UserService;

// 导出控制器包

exports com.example.controller;

}

  • UserController.java

package com.example.controller;

import com.example.model.User;

import com.example.service.api.UserService;

import java.util.ServiceLoader;

public class UserController {

private final UserService userService;

// 初始化:通过ServiceLoader加载服务实现

public UserController() {

this.userService = ServiceLoader.load(UserService.class)

.findFirst()

.orElseThrow(() -> new RuntimeException("未找到UserService实现"));

}

// 控制器方法:获取用户信息

public User getUserById(Long userId) {

return userService.getUserInfo(userId);

}

}

  • pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<parent>

<groupId>com.example</groupId>

<artifactId>e-commerce-modular</artifactId>

<version>1.0.0</version>

</parent>

<modelVersion>4.0.0</modelVersion>

<artifactId>controller</artifactId>

<name>controller</name>

<dependencies>

<dependency>

<groupId>com.example</groupId>

<artifactId>service</artifactId>

<version>1.0.0</version>

</dependency>

</dependencies>

</project>

(4)编译与运行
  1. 编译项目:在父模块根目录执行mvn clean package,生成各模块的 Jar 包;
  1. 运行控制器模块:通过模块路径指定依赖的 Jar 包,执行UserController:

java --module-path controller/target/controller-1.0.0.jar:service/target/service-1.0.0.jar:dao/target/dao-1.0.0.jar:model/target/model-1.0.0.jar --module com.example.controller/com.example.controller.UserController

3.2 场景 2:模块间服务协作(基于服务发现机制)

需求:电商项目新增 “订单服务” 模块,要求:

  1. 订单服务依赖用户服务(获取用户信息);
  1. 订单服务与用户服务通过服务发现机制协作,无直接模块依赖;
  1. 支持后续替换用户服务的实现,无需修改订单服务代码。
实现方案:
  1. 新增com.example.order模块,依赖com.example.service.api(用户服务接口模块),使用uses声明使用UserService;
  1. 订单服务模块的module-info.java

module com.example.order {

// 依赖用户服务接口模块

requires com.example.service.api;

// 依赖实体类模块(传递依赖)

requires transitive com.example.model;

// 使用UserService服务

uses com.example.service.api.UserService;

// 导出订单服务接口包

exports com.example.order.api;

// 提供订单服务实现

provides com.example.order.api.OrderService with com.example.order.impl.OrderServiceImpl;

}

  1. 订单服务实现类

package com.example.order.impl;

import com.example.model.User;

import com.example.order.api.OrderService;

import com.example.service.api.UserService;

import java.util.ServiceLoader;

public class OrderServiceImpl implements OrderService {

private final UserService userService;

public OrderServiceImpl() {

this.userService = ServiceLoader.load(UserService.class).findFirst().orElseThrow();

}

@Override

public String createOrder(Long userId, Long productId) {

// 调用用户服务获取用户信息

User user = userService.getUserInfo(userId);

// 模拟创建订单

String orderId = "ORDER_" + System.currentTimeMillis();

return String.format("订单创建成功:订单ID=%s,用户=%s,商品ID=%d",

orderId, user.getUsername(), productId);

}

}

优势:
  • 解耦协作:订单服务仅依赖用户服务接口,不依赖具体实现,实现 “接口与实现分离”;
  • 可替换性:若后续用户服务实现变更(如从本地 DAO 改为远程 RPC 调用),仅需替换服务实现模块,无需修改订单服务代码;
  • 可测试性:单元测试时可提供 Mock 的UserService实现,隔离外部依赖。

3.3 场景 3:传统项目的模块化迁移(分步迁移策略)

需求:将一个传统的非模块化电商项目(基于 Java 8)迁移到 Java 17 模块化系统,要求:

  1. 迁移过程不影响现有功能,支持逐步迁移;
  1. 先将核心模块(如model、dao)模块化,再迁移上层模块(如service、controller);
  1. 兼容项目中的传统 Jar 依赖(如 Spring、MyBatis)。
迁移步骤:
(1)环境准备
  • 升级 JDK:将项目 JDK 从 8 升级到 17,确保代码兼容 Java 17 语法;
  • 升级构建工具:将 Maven 升级到 3.8+,支持 Java 17 模块化。
(2)分步迁移核心模块
  1. 第一步:迁移model模块
    • 在model模块根目录创建module-info.java,定义模块名称和导出包;
    • 修改 Maven 依赖,将model模块打包为模块化 Jar;
    • 其他模块暂时通过classpath依赖model模块的模块化 Jar(自动模块)。
  1. 第二步:迁移dao模块
    • 为dao模块添加module-info.java,声明依赖model模块(模块化依赖);
    • 导出 DAO 接口包,隐藏实现包;
    • 其他模块(如service)改为通过模块路径依赖dao模块。
  1. 第三步:迁移上层模块
    • 依次迁移service、controller模块,逐步替换classpath依赖为模块路径依赖;
    • 对传统 Jar 依赖(如 Spring),通过自动模块方式引入,后续逐步替换为官方提供的模块化 Jar(如 Spring 6 + 已支持模块化)。
(3)兼容传统依赖
  • 自动模块处理:对于未模块化的传统 Jar(如 MyBatis 3.5.x),通过模块路径加载,Java 自动转换为自动模块,模块名由 Jar 包名推导(如mybatis-3.5.10.jar→mybatis);
  • classpath兼容:若部分模块暂未迁移,可通过--class-path参数保留传统classpath,但建议优先使用模块路径。
(4)验证与测试
  • 编译验证:使用javac --module-path编译模块化模块,确保依赖正确;
  • 运行验证:使用java --module-path运行项目,验证功能正常;
  • 兼容性测试:测试传统功能与模块化模块的交互,确保无兼容性问题。
迁移原则:
  • 渐进式迁移:避免一次性迁移所有模块,优先迁移低耦合的核心模块,降低风险;
  • 兼容性优先:对于无法模块化的传统依赖,通过自动模块兼容,不强制替换;
  • 测试驱动:每迁移一个模块,执行单元测试和集成测试,确保功能正常。

四、Java 模块化系统的优势与注意事项

4.1 核心优势

  1. 依赖清晰:显式声明模块依赖,避免隐式依赖和版本冲突,解决 “Jar 地狱”;
  1. 封装性强:仅导出需要对外暴露的包,内部实现细节隐藏,降低 API 维护成本;
  1. 架构可控:编译期和运行期强制模块边界,防止跨模块非法访问,保障架构设计落地;
  1. 部署高效:支持模块化打包,可仅部署项目依赖的模块,减少冗余资源;
  1. 服务化协作:基于服务发现机制,实现 “接口与实现
http://www.dtcms.com/a/605775.html

相关文章:

  • Claude Code 深度解析:架构、工作原理与常见误解
  • 珠海市企业网站制作品牌仿简书wordpress博客主题
  • 文化传媒 网站设计成都网站建设:
  • Python实用指南:python + pyqt
  • SSM基于J2EE的山西旅游网站的设计与实现iiqmx(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • 如何通过 WebSocket 接入期货实时行情接口
  • 开源 Objective-C IOS 应用开发(六)Objective-C 和 C语言
  • 网站栅格安装网站模版视频
  • PHP While 循环
  • Docker 部署 DeepSeek-OCR 和WebUI
  • 长沙h5网站建设什么软件可以发布广告信息
  • 如何保证数据库与 Redis 的数据一致性
  • redis连接服务
  • Linux systemd闲谈杂话(第一篇:概述)
  • Spring 核心技术解析【纯干货版】- XII:Spring 数据访问模块 Spring-R2dbc 模块精讲
  • 手机什么网站可以设计楼房关于网站建设的调查问卷
  • 零基础网站建设教学申请自己邮箱域名
  • JVM 内存结构、堆细分、对象生命周期、内存模型全解析
  • 网络安全编程——基于Python实现的SSH通信(Windows执行)
  • WAF防护:应用层安全的核心堡垒
  • 【OpenCV图像处理】图像去噪:cv.fastNlMeansDenoising()
  • 基于AI Agent模板:快速生成 SQL 测试数据
  • 无锡网站建设方案企业计划书
  • 做购票系统网站网站开发推广方案策划书
  • JVM GC 垃圾回收体系完整讲解
  • JVM 内存结构的详细介绍
  • Linux命令-egrep命令(文本搜索工具)
  • 《Flutter全栈开发实战指南:从零到高级》- 14 -网络请求与数据解析
  • 模板网站配置文件seo难不难
  • div2 1052 个人补题笔记