Java花样玩法-打破反射调用限制

问题

反射调用内部API时会报错

Exception in thread "main" java.lang.IllegalAccessException: class cn.lingex.Main (in module maven.lib.test) cannot access class jdk.internal.misc.Unsafe (in module java.base) because module java.base does not export jdk.internal.misc to module maven.lib.test
	at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:398)
	at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:709)
	at java.base/java.lang.reflect.Method.invoke(Method.java:571)
	at maven.lib.test/cn.lingex.Main.main(Main.java:42)

原因是java9后增加了模块化检查,此时需要setAccessible关闭检查。

Exception in thread "main" java.lang.reflect.InaccessibleObjectException: Unable to make public static jdk.internal.misc.Unsafe jdk.internal.misc.Unsafe.getUnsafe() accessible: module java.base does not "exports jdk.internal.misc" to module maven.lib.test
	at java.base/java.lang.reflect.AccessibleObject.throwInaccessibleObjectException(AccessibleObject.java:388)
	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:364)
	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:312)
	at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:203)
	at java.base/java.lang.reflect.Method.setAccessible(Method.java:197)

调用setAccessible也会报错!大致意思是模块java.base中的jdk.internal.misc包没有导出到我自己的模块,也有检查。
虽然使用启动参数能够解决,但是不够优雅,如果你提供依赖给别人让别人加启动参数才能用。所以我们需要通过代码解决

解决

  • debug调试下发现在Method做了最终的校验checkCanSetAccessible(),校验有很多,我们找能够操作的地方在这里插入图片描述

如图,在这个校验中使用了调用所在类的模块与方法所在类的模块进行了对比,若相等则返回true,我们从这里入手。将调用时所在的类模块替换成方法所在类模块

    public static void main(String[] args) throws Exception {
        Class<?> unsafeClass = Class.forName("jdk.internal.misc.Unsafe");
        Method method = unsafeClass.getMethod("getUnsafe");
        // 获取Unsafe所在模块
        Module unsafeModule = unsafeClass.getModule();
        // 获取当前模块
        Module currentModule = Main.class.getModule();
        // 替换Main.class所在模块
        UnsafeObjectModify.setObjectFieldValueRef(Main.class,"module",unsafeModule);
        method.setAccessible(true);
        // 设置完成后替换回来
        UnsafeObjectModify.setObjectFieldValueRef(Main.class,"module",currentModule);
        // 获取到unsafe对象
        Object unsafe = method.invoke(unsafeClass);
        System.out.println(unsafe); // jdk.internal.misc.Unsafe@3eb07fd3
    }
  • 另一种方法:查看setAccessible最后做了什么
    在这里插入图片描述

它调用了父类AccessibleObject的setAccessible0方法,将成员override设置成了我们给的值,我们更改下代码

public static void main(String[] args) throws Exception {
        Class<?> unsafeClass = Class.forName("jdk.internal.misc.Unsafe");
        Method method = unsafeClass.getMethod("getUnsafe");
        // 将method的override成员设置为true(等同于无校验的method.setAccessible(true))
        UnsafeObjectModify.setObjectFieldValueBool(method,"override",true);
        Object unsafe = method.invoke(unsafeClass);
        System.out.println(unsafe); // jdk.internal.misc.Unsafe@3eb07fd3
}

用到的依赖

<dependency>
    <groupId>io.github.lngex</groupId>
    <artifactId>object-unsafe</artifactId>
    <version>1.0.1</version>
</dependency>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值