DebugEN
科技森
专注于Java开发~每天都会更新文章~
  1. 首页
  2. Java
  3. 正文

[翻译] 反射的用法——用Java调用私有方法

2021年03月22日 2297点热度 0人点赞 0条评论 作者: kejisen

概述

虽然在Java中将方法设为private,以防止从拥有类的外部调用它们,但出于某些原因,我们可能仍需要调用它们。

为此,我们需要解决Java的访问控制问题。这可以帮助我们到达库的某个角落,或者允许我们测试一些通常应保密的代码。

在这个简短的教程中,我们将研究如何验证方法的功能,而不考虑其可见性。我们将考虑两种不同的方法:Java Reflection API和Spring的ReflectionTestUtils。

可见性超出我们的控制

对于我们的示例,让我们使用对长数组进行操作的实用程序类LongArrayUtil。我们的类有两个indexOf方法:

public static int indexOf(long[] array, long target) {
    return indexOf(array, target, 0, array.length);
}

private static int indexOf(long[] array, long target, int start, int end) {
    for (int i = start; i < end; i++) {
        if (array[i] == target) {
            return i;
        }
    }
    return -1;
}

假设这些方法的可见性无法更改,但是我们要调用私有indexOf方法。

Java反射API

用反射找到方法

虽然编译器阻止我们调用类中不可见的函数,但我们可以通过反射来调用函数。首先,我们需要访问描述我们要调用的函数的Method对象:

Method indexOfMethod = LongArrayUtil.class.getDeclaredMethod(
  "indexOf", long[].class, long.class, int.class, int.class);

我们必须使用getDeclaredMethod才能访问非私有方法。我们在具有函数的类型(在本例中为LongArrayUtil)上调用它,并传入参数的类型以标识正确的方法。

如果该方法不存在,则该函数可能会失败并引发异常。

允许访问方法

现在,我们需要暂时提高方法的可见性:

indexOfMethod.setAccessible(true);

此更改将持续到JVM停止或可访问属性设置回false为止。

反射调用方法

最后,我们在Method对象上调用invoke:

int value = (int) indexOfMethod.invoke(
  LongArrayUtil.class, someLongArray, 2L, 0, someLongArray.length);

现在,我们已经成功访问了私有方法。

要调用的第一个参数是目标对象,其余参数需要匹配我们方法的签名。在这种情况下,我们的方法是静态的,目标对象是父类– LongArrayUtil。对于调用实例方法,我们将传递要调用其方法的对象。

我们还应注意, invoke返回 Object,对于 void函数,该参数为 null,并且需要转换为正确的类型才能使用它。

Spring ReflectionTestUtils

到达类的内部是测试中的常见问题。Spring的测试库提供了一些捷径,以帮助单元测试达到类。这通常可以解决特定于单元测试的问题,在单元测试中,测试需要访问一个私有字段,Spring可能会在运行时实例化该私有字段。

首先,我们需要在pom.xml中添加spring-test依赖项:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.3.4</version>
    <scope>test</scope>
</dependency>

现在,我们可以在ReflectionTestUtils中使用invokeMethod函数,该函数 使用与上述相同的算法,从而节省了编写大量代码的时间:

int value = ReflectionTestUtils.invokeMethod(
  LongArrayUtil.class, "indexOf", someLongArray, 1L, 1, someLongArray.length);

由于这是一个测试库,因此我们不希望在测试代码之外使用它。

注意事项

使用反射绕过功能可见性会带来一些风险,甚至可能无法实现。我们应该考虑:

Java安全管理器是否将在我们的运行时允许这样做
在没有编译时检查的情况下,我们正在调用的函数在将来是否会继续存在
重构我们自己的代码,使事物更清晰可见

总结

在本文中,我们研究了如何使用Java Reflection API和Spring的ReflectionTestUtils访问私有方法。

标签: java 反射
最后更新:2021年03月22日

kejisen

保持饥渴的专注,追求最佳的品质

点赞
< 上一篇
下一篇 >

文章评论

取消回复
最新 热点 随机
最新 热点 随机
【原创】记录一次失败的折腾——使用jkeymaster实现的按键监听 【原创】这些年我用过的IDEA插件 【原创】在windows上使用VNC远程连接linux桌面 我在RxJava使用线程池时遇到的问题 [原创文章] Swagger生成pdf格式的接口文档 [个人翻译]Java HTTP工具类的客户端证书认证 [原创] 如何从 Git 的提交历史记录中删除大文件 [翻译] 创建一个只读的Repository接口(Spring Data) [翻译] 反射的用法——用Java调用私有方法 Java 虚拟机最多可以支持多少个线程? 排查Hibernate的慢查询日志–这是查找慢查询的最简单方法 [翻译] 使用apache poi在excel文件中插入一行数据 [翻译] 在Spring 中@EntityScan与@ComponentScan注解有什么区别 [原创] 从QQ音乐网页版扒歌词的补充说明 [原创] 介绍java maven项目的多种打包方式 原创——在Java中生成随机数 将G1垃圾回收的内存使用量减少20%(翻译) [原创] java8 lambda表达式的toMap造成的空指针异常 [原创] 在Spring Boot中使用CommandLineRunner来在启动时执行代码 [转载] Kafka 节点重启失败导致数据丢失的分析排查与解决之道
在Java中生成随机的日期 Linux截图软件推荐-flameshot 【原创】这些年我用过的IDEA插件 [原创] 在ubuntu18.04上安装chromedriver java maven项目的几种打包方式 [翻译] 反射的用法——用Java调用私有方法 使用Spring RestTemplate压缩请求 Java 虚拟机最多可以支持多少个线程? [翻译] 创建一个只读的Repository接口(Spring Data) 中通Elasticsearch集群运维实践 [翻译] 在Spring 中@EntityScan与@ComponentScan注解有什么区别 [原创] spring-boot返回json或者xml格式的数据 [原创] 在Spring Boot中使用CommandLineRunner来在启动时执行代码 [原创] 介绍java maven项目的多种打包方式 我在RxJava使用线程池时遇到的问题 将G1垃圾回收的内存使用量减少20%(翻译) [原创] 如何从 Git 的提交历史记录中删除大文件 [原创] java8 lambda表达式的toMap造成的空指针异常 [个人翻译]Java HTTP工具类的客户端证书认证 Spring Boot项目修改Tomcat端口号
标签聚合
maven springboot 歌词 qq音乐 elasticsearch java json spring base64 linux

COPYRIGHT © 2020 Kejisen. ALL RIGHTS RESERVED.

THEME KRATOS MADE BY VTROIS