java - Java - 查找从类加载器返回特定类型的所有方法
问题描述
我正在寻找一种方法来从我的类加载器中找到所有类的所有方法,该类加载器返回某种类型。例如List
.
我尝试使用这个库 - https://github.com/ronmamo/reflections
Reflections reflections = new Reflections("com",new MethodParameterScanner());
Set<Method> methodsReturn = reflections.getMethodsReturn(List.class);
methodsReturn
.forEach(c -> System.out.println(c.getDeclaringClass() + "-" + c.getName()));
但因这个例外而失败。
org.reflections.ReflectionsException: could not get type for name com.intellij.rt.execution.CommandLineWrapper$AppData.access
at org.reflections.ReflectionUtils.forName(ReflectionUtils.java:312)
at org.reflections.util.Utils.getMemberFromDescriptor(Utils.java:67)
at org.reflections.util.Utils.getMethodsFromDescriptors(Utils.java:88)
有没有其他方法可以做到这一点?
解决方案
你想要的是不可能的。反射是一种泄漏的抽象;没有保证的黑客攻击。
问题是这ClassLoader
是一个抽象,它基本上只有以下可用的奇异操作:
- 给定资源名称,返回类加载器可以找到的所有版本的资源;通常,几乎总是,这将是 0 或 1 个项目。“资源”是一个 URL;
InputStream
一个可以保证被这个加载器变成一个。
就是这样。
请注意,没有方法可以请求所有资源的列表,因此,任何尝试从类加载器中“获取所有具有属性 X 的资源”的尝试都被破坏了。这就像试图从石头上取水一样。你无法得到那里没有的东西。
反射黑客所做的是检查它是哪种类加载器。如果它将类加载器识别为从具有列出所有操作的位置(例如 jar 文件或文件系统目录)获取资源的事物,它将获取底层位置并对这些资源执行直接“列表”操作,但是,如果所涉及的类加载器不是反射识别的类加载器,或者不是由具有列表操作的存储支持的类加载器,那么反射要么默默地忽略它,要么崩溃。
这就引出了一个必然的结论:你不想做你说你想做的事;java不支持它。
即使您接受除非仅使用基于基本 jar/dir 的类加载器,否则您的程序将失败,您正在做的是将 java 转换为结构类型的语言。Java 不是结构类型的;它是围绕名义打字设计的。如果您效仿,Java 不仅会更好地工作,而且您正在做其他编写 Java 代码的人认为永远不会发生的事情。
例如,如果您说:“我正在寻找各种相机,所以我将扫描整个类路径以查找具有签名方法的任何类,并void shoot(Person p)
在自拍选项列表中提供它”,那么您当课程以某种方式最终出现在您的类路径中时,您将度过一段非常糟糕的时光。Gun
在 java 中,两个方法基本上是不相关的,即使它们具有相同的名称 - 方法由 ENTIRE 签名定义,包括它所在的类型。这一事实贯穿于 java 的语言设计中。
那么,你如何在java中做这样的事情呢?
您可能想要的是 SPI。
- SPI 现在和永远都与所有类加载器 100% 兼容。
- Java 本身在很多地方都使用了 SPI。
- SPI 很常见,以至于许多 IDE 和构建系统中都内置了插件和工具。
- 它仅适用于名义打字。
以下是 SPI 的工作原理:
- 定义一个接口,或者如果必须的话,定义一个(抽象)类。
- “插件”将做两件事:
- [1] 创建一个可实例化的类(公共的,非抽象的,有一个公共的无参数构造函数),[2] 实现/扩展我们在第一步中创建的类型,并且 [3] 列出它的完全限定名称在一个名为
META-INF/services/com.foo.full.package.name.ThatInterfaceFromStep1
.
然后,主要工具执行以下操作:
- 向类加载器询问与name 匹配的所有
META-INF/services/com.foo.full.package.name.ThatInterfaceFromStep1
资源,然后从所有这些中读取所有行。 - 然后它将以这种方式找到的每个名称进行类加载,并将它们作为实现提供。您可以将所有这些视为 的实例
ThatInterfaceFromStep1
,因为它们是。不会发生意外的铅中毒。
您可以使用注释处理器,这样您所要做的就是编写,例如:
@Provides(ImageSaver.class)
public class Jpg2000Saver implements ImageSaver {
@Override public String description() {
return "Joint Photographic Experts Group 2000 (JP2)";
}
@Override public void write(Image image, OutputStream target) throws IOException {
// .... implement here
}
}
并且相关的注释处理器将负责为您制作该 META-INF 文件。或者,自己制作该文件;例如,如果您使用 maven,请将其放入src/resources
. 要“加载” SPI 文件,它内置于 java 本身:ServiceLoader。
推荐阅读
- jquery - Jquery输入类型切换功能
- jolt - 来自数组的 JOLT 转换
- delphi - 如何在运行时控制视觉控件的排列?
- sql - 在 VBA 中出现“预期:语句结束”错误
- javascript - 如何在控制台上从 utf 代码编写“键帽数字一个”=1️⃣?
- html - 无法显示此版本的引导程序的日期时间选择器
- ios - 应用程序在应用商店被拒绝,因为苹果登录没有发生
- ruby - 在 mint 中使用 rbenv 的 Ruby 2.3.8 安装错误 | uncommon.mk:203:目标“build-ext”的配方失败
- python - Python open cv2 ip web cam 无法正常工作,显示错误
- tensorflow - 从 Python 转换为 Tensorflow.js 后的 model.predict() 结果不同