Java模块化系统要求显式声明依赖(requires)与导出(exports),注解扫描、APT处理及Spring组件扫描均受模块边界限制,未导出包中的注解类不可见,反射访问私有成员还需opens指令。

Java模块化系统(JPMS)通过 module-info.java 显式声明依赖与导出,从根本上改变了类路径时代“全量可见”的注解扫描逻辑。注解处理器、Spring等框架的类扫描行为不再自动跨模块生效,必须满足模块系统的可读性(requires)与包可访问性(exports)双重约束。
模块声明决定注解能否被其他模块“看到”
一个模块若未在 module-info.java 中 exports 包,即使该包内存在带 @Component、@Entity 或自定义注解的类,其他模块也无法在编译期或运行期通过反射访问这些类——JVM 会直接抛出 IllegalAccessError 或导致 Class.forName() 失败。例如:
-
错误写法:模块
com.example.data定义了com.example.data.model.User并标注@Entity,但未exports com.example.data.model; -
正确做法:在
module-info.java中添加exports com.example.data.model;,且使用方模块需声明requires com.example.data;
注解处理器需显式依赖并读取目标模块
APT(Annotation Processing Tool)在模块化环境下无法自动发现其他模块中的注解。若自定义注解 @AutoLog 定义在模块 com.example.annotation 中,其处理器必须在 module-info.java 中 requires com.example.annotation;,否则 RoundEnvironment.getElementsAnnotatedWith() 返回空集合。同时注意:
- 处理器模块应
requires static注解模块(避免运行时强依赖) - 注解本身需用
@Retention(RetentionPolicy.SOURCE)或.CLASS,不能是.RUNTIME除非明确需要反射读取 - Maven 中需配置
maven-compiler-plugin的annotationProcessorPaths显式引入处理器模块
Spring Framework 在 JPMS 下的扫描限制
Spring 5.3+ 支持模块化,但默认组件扫描(@ComponentScan)仅作用于当前模块的已导出包。即使使用 classpath*:,也无法突破模块边界加载未导出类。实际开发中常见问题包括:
立即学习“Java免费学习笔记(深入)”;
-
@Service类在模块 A 中定义且未exports其包 → Spring 启动时报 “No beans of type X found” - 跨模块
@Configuration类被忽略,因配置类所在包未导出,Spring 无法反射调用getMethods() - 解决方案:在提供方模块中
exports配置类和组件所在包;消费方模块requires该模块,并在@ComponentScan中指定完整包名(如"com.example.service")
运行时反射扫描需额外开放(Open)而非仅导出
当框架(如 Hibernate、Jackson)需通过反射访问私有字段或内部类时,仅 exports 不够,必须使用 opens(或 open module)。例如:
- Hibernate 实体类含
private String name;→ 模块需opens com.example.data.model to org.hibernate.orm.core; - 若使用
@JsonCreator构造器,Jackson 需反射调用私有构造方法 → 对应包需opens给com.fasterxml.jackson.databind -
opens是运行时指令,不影响编译,但未配置会导致InaccessibleObjectException

















