You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Java模块中使用Spring @Validated且避免InaccessibleObjectException

异常根因

该异常属于Java模块化体系的访问权限限制:@Validated注解触发的参数校验逻辑默认由无模块化声明的Hibernate Validator实现,该依赖会被加载到未命名模块中,运行时需要反射访问com.example.demo.ConfigProperties的包私有字段,但当前模块未开放对应包的访问权限给未命名模块,因此抛出异常。


问题1:调试定位未命名模块对应的依赖库

有两个可落地的定位方法:

  • 新增JVM启动参数打印模块加载详情:启动时添加参数-Djdk.module.showModuleResolution=true,控制台会输出所有模块的加载明细,包括未命名模块加载的Jar包来源,匹配报错信息里的@163370c2标识符对应的类加载器加载的Jar即可定位到目标依赖。
  • 断点调试定位:在java.lang.reflect.InaccessibleObjectException的构造方法上打断点,触发异常后查看调用栈,找到发起反射调用的类所属的Jar包,就是对应的依赖库。

该场景下90%以上的情况对应的依赖是Hibernate Validator,它是JSR-380校验规范的默认实现,绝大多数版本都没有提供模块化声明,会被加载到未命名模块中。


问题2:不开放整个模块的前提下解决异常

以下三种方案都不需要开放整个模块,可按需选择:

方案1:规范配置类写法(最推荐)

给ConfigProperties类的所有字段添加public的getter和setter方法,修改后代码示例:

package com.example.demo;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.annotation.Validated;

import javax.validation.constraints.NotNull;

@Configuration
@ConfigurationProperties("abc")
@Validated
public class ConfigProperties {
    @NotNull 
    private String hello;
    @NotNull 
    private String byeBye;

    public String getHello() {
        return hello;
    }

    public void setHello(String hello) {
        this.hello = hello;
    }

    public String getByeBye() {
        return byeBye;
    }

    public void setByeBye(String byeBye) {
        this.byeBye = byeBye;
    }

    @Override
    public String toString() {
        return "ConfigProperties{hello='" + hello + '\'' +", byeBye='" + byeBye + '\'' + '}';
    }
}

这种方案下Spring参数绑定通过setter方法完成,校验通过getter方法完成,不需要反射访问类字段,不会触发模块访问限制,不需要修改任何模块权限配置。

方案2:精准开放指定包给未命名模块

如果不想修改配置类代码,可以在module-info.java中只开放com.example.demo这一个包给所有未命名模块,模块下其他包的权限不受影响:

module com.example.demo {
    // 只开放指定包给Spring相关命名模块和所有未命名模块,其他包保持封闭
    opens com.example.demo to spring.beans, spring.boot, spring.core, ALL-UNNAMED;
    exports com.example.demo;

    requires java.validation;
    requires org.slf4j;
    requires spring.beans;
    requires spring.boot;
    requires spring.boot.autoconfigure;
    requires spring.core;
    requires spring.context;
}

ALL-UNNAMED是JDK模块化的官方关键字,代表所有未命名模块,不会开放给其他无关命名模块,权限控制依然精准。

方案3:用JVM参数临时配置权限

如果不想修改代码和模块配置,可以添加JVM启动参数实现临时的权限开放:
--add-opens com.example.demo/com.example.demo=ALL-UNNAMED
该参数只在运行时生效,不会影响代码的模块化声明。


内容的提问来源于stack exchange,提问作者vab2048

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 20:54:07