前提
项目开始使用springboot3了,所以java的版本也升级了,用的是java17,该版本是一个长期维护的版本,所以没有用java19
版本差异介绍
从 Java 9 开始,setAccessible() 方法被标记为 @Deprecated,并且引入了模块化系统
//java9代码
//代码从chatgpt获取,如果不对请留言提供一下代码
@Deprecated(since="9")
public final void setAccessible(boolean flag) throws SecurityException {
if (flag == accessible) { // 判断是否和当前权限状态一致
return;
}
if (flag) {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkPermission(accessPermission);
}
}
checkPermissionFromStack(); // 检查权限
accessible = flag;
}
java11开始,因为安全问题,部分包,类已经不可以被反射并设置值,反射需要在java启动的时候添加相应的命令,例如
–add-opens java.base/java.lang=ALL-UNNAMED
–add-opens java.base/java.lang.reflect=ALL-UNNAMED
//java17代码
@Override
@CallerSensitive
public void setAccessible(boolean flag) {
AccessibleObject.checkPermission();
if (flag) checkCanSetAccessible(Reflection.getCallerClass());
setAccessible0(flag);
}
java11 setAccessible方法说明
上述代码最终调用的是AccessibleObjectcheckCanSetAccessible方法,该方法会做一些列的判断,例如
- 调用类和Field中声明的类是否是同一个
- 是否是Object类
- private是否是在一个类中
- protected是否在同一个包下
- 被调用类是否是final修饰的
- Field中声明的类是否是final修饰的
- Field中声明的类是否不是命名的
- Field中生命的类所在的包是否被排除
- 被反射的类是否对调用的类的开放
暂时试出来的被修饰后不可以反射的有被保护的包,final修饰的类/字段/方法,volatile,native,transient,其他的暂时不清楚
如何用代码的方式对被保护的类进行反射呢?
暂时的答案是做不到
这个是其他人写的,我按照这个方式是不可以的
一种适用于 Java 8 至 Java 17 的反射修改 static final 属性的方法
解决方案
上代码
下边的代码可以在java8中可以使用
如果需要在java17使用,需要添加其他参数,继续往下看
@SneakyThrows
public static <T extends Annotation> void setValueToAnnotate(T annotation, String valueName, Object value) {
InvocationHandler invocationHandler = null;
if (Objects.nonNull(annotation)) {
invocationHandler = Proxy.getInvocationHandler(annotation);
}
if (Objects.isNull(invocationHandler)) {
return;
}
Field nameField = invocationHandler.getClass().getDeclaredField("memberValues");
nameField.setAccessible(true);
Map<String, Object> memberValues = (Map<String, Object>) nameField.get(invocationHandler);
memberValues.put(valueName, value);
}
在java17中,需要在java命令后边添加启动参数
–add-opens java.base/sun.reflect.annotation=ALL-UNNAMED
- –add-opens 允许指定的模块下的包可以被其他模块以反射的方式访问
- java.base是模块名,也代表了java核心库模块
- sun.reflect.annotation 是一个要打开访问权限的包路径
- ALL-UNNAMED 是一个表示所有模块的未命名模块的特殊标识。
如果要在idea中使用,需要再Run/Debug Configurations界面中添加信息
点击Modify options
选择Add VM options
复制命令到文本框中,启动项目即可
引用文章:
- Java高级——模块化系统
https://blog.csdn.net/qq_35258036/article/details/127645479 - 一种适用于 Java 8 至 Java 17 的反射修改 static final 属性的方法
https://blog.csdn.net/wu_weijie/article/details/129251045
可参考文章:
- Java动态修改注解值
https://blog.csdn.net/m0_45097637/article/details/123708278