setAccessible(true)绕过的是JVM的SecurityManager访问控制链路,而非语法可见性判断;它通过设置override标志位跳过每次反射调用时的完整权限校验,提升性能并允许访问私有成员,但在Java 9+模块系统下可能因包未开放而抛InaccessibleObjectException。

setAccessible(true) 绕过了什么检查
它绕过的是 JVM 的 SecurityManager 访问控制链路,而不是简单的“语法可见性判断”。每次通过反射调用 Method.invoke() 或读写 Field 时,JVM 默认会执行完整的访问权限校验:检查调用方类是否在同一个包、是否有继承关系、是否被声明为 public 等。这个校验发生在每次调用时,不是一次性的。
调用 setAccessible(true) 后,反射对象(Method/Field/Constructor)内部的 override 标志位被设为 true,后续所有对该对象的操作跳过整个校验流程——相当于把“安检通道”直接关闭,只走快速通行道。
为什么 public 方法调用也受益
很多人误以为只有 private 成员才需要 setAccessible(true),其实不然。即使目标方法是 public,只要它是通过 getDeclaredMethod() 获取的(而非 getMethod()),默认 isAccessible() 返回 false,仍会触发完整校验。
-
getMethod("xxx")返回的对象默认isAccessible() == true,不校验 -
getDeclaredMethod("xxx")返回的对象默认isAccessible() == false,必校验 - 框架如 Spring、Jackson 大量使用
getDeclaredXXX,因为要访问private字段/方法,所以几乎总得手动setAccessible(true)
性能差异到底有多大
实测中,对同一个 Method 对象连续调用 1000 万次:
Simple : 2480ms setAccessible(true) : 720ms
加速比接近 3.4×,且差距随调用频次线性放大。关键点在于:
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
- 校验逻辑本身有方法栈开销、类加载器查找、包名比对等步骤
- JIT 编译器对带校验路径的方法难以充分内联优化
-
setAccessible(true)后的调用路径更短、更稳定,更容易被 JIT 识别为热点并深度优化
容易忽略的兼容性与风险点
从 Java 9 开始,模块系统(JPMS)引入了更强的封装约束:setAccessible(true) 在跨模块访问非导出(non-exported)包中的类成员时,会直接失败,抛出 InaccessibleObjectException,不再给 SecurityManager 插手机会。
这意味着:
- Java 11+ 默认无
SecurityManager,但模块限制反而更硬 -
--add-opens java.base/java.lang=ALL-UNNAMED这类启动参数不是可选优化项,而是必要前提 - 某些 JDK 发行版(如 GraalVM Native Image)完全禁用
setAccessible(true),需提前做反射配置
真正卡住人的从来不是“要不要加”,而是“加了之后在哪些环境里根本不起作用”。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










