【问题标题】:Is there are any way to get all the instances of type X by reflection utils有什么方法可以通过反射工具获取所有类型 X 的实例
【发布时间】:2019-09-02 14:08:56
【问题描述】:

我正在尝试找到一种方法来通过 ReflectionUtil 获取所有类型 X 的实例。

A extends of X: 
   List<X> xList;
   X arg1;
   int size;

   A(List<X> xList, X arg1, int size){
      this.xList = xList;
      this.arg1= arg1;
      this.size= size;
   }

B extends of X:
   String argB;
   String arg2

C extends of X:
   X argC;

初始化:

    List<X> list = new ArrayList();
    B item1 = new B("item1", "1");
    B item2 = new B("item2", "2");
    C c1 = new C(item1);
    C c2 = new C(item2);
    list.add(c1);
    list.add(c2);
    B b = new B("test", "3");
    A myAClass = new A(list, b, 5);

我可以访问 myAClass 对象,并且我想从 myAClass 沿类层次结构获取所有类型 X 的实例。

在上面的例子中,myAClass 初始化后的样子:

我期望的结果是: [item1 , item2 , c1, c2, b] - 所有类型 X 的实例。

【问题讨论】:

  • 你能再解释一下吗?你问的是获取所有字段,但在预期的结果中你在谈论B - an item of xList,我不太明白你想要实现什么。也许尝试编写一些实际的示例代码,这样我们也可以复制和测试东西。
  • 我正在尝试获取所有字段类以及嵌套类。
  • 对不起,我还是无法理解,您能否提供示例测试用例(包含所有示例类、数据和结果)?这将有助于了解您在寻找什么。
  • 好的,现在我明白你想要做什么,但我不确定你所说的“ReflectionUtil”是什么意思。但否则这个问题很复杂,你能不能解释一下你为什么需要这个?你到底在做什么,你需要创建这样奇怪的扫描。他们还有什么安全的假设可以做吗? like 类只是简单的数据对象,可能包含列表和映射,否则需要考虑很多边缘情况 - 这就是为什么它看起来像一些糟糕的设计,可能应该以不同的方式解决。
  • 我的目的是在 json 反序列化后通过反序列化 json 来自动装配 beans 和 Value 属性,然后调用 postProcess 方法来自动装配 beans:applicationContext.getAutowireCapableBeanFactory().autowireBeanProperties(clzz, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true)。因此,我需要找到所有可以包含 autowire 值的类,并且这些类必须是 X 类型。在这里我解释了我的真正问题:stackoverflow.com/questions/57753753/…

标签: java spring reflection


【解决方案1】:

您“只是”需要使用递归搜索,但这样扫描时需要担心很多事情,因为您不知道这种结构可能有多深和复杂。
还有一些关于扫描 Java 类型(如列表和映射)的决定,将来对此类内部的反射访问可能会受到限制,因此最好在可能的情况下跳过反射 - 如果字段是列表/设置/可迭代类型然后只需使用普通的 java 代码对其进行迭代。但是,您要么需要检查给定列表是否为简单的知名类型,如 ArrayList,要么记住您的扫描代码可能有一些副作用,例如,如果您要遍历从某些数据流中读取的某些资源。
另外要记住的是循环引用,因为在扫描时您可能最终会遇到您已经在其中的一个对象,并引入无限循环。为了防止这种情况,我们可以将已扫描的对象添加到 Set 或 IdentitySet - 取决于您是否关心实际的循环引用,或者您还想跳过扫描不同实例的相同对象。
还有一件事 - 实现某种过滤器可能是个好主意,如果您最终会遇到一些 java 内部对象,则停止扫描,例如在使用反射扫描某种并发集合时,您可能最终会扫描内部使用的一些内部对象实现它。

在解释了几个更大的问题之后,我希望您考虑一下,如果您真的想要这样的解决方案,也许它是 xyproblem 并且您实际上正在尝试实现的更好的解决方案。

因此,您需要根据自己的需要和可以做出的假设调整此代码,但您可以以此为例:

private static <T> void scanInstance(Object objectToScan, Class<T> lookingFor, Set<? super Object> scanned, Collection<? super T> results) {
    if (objectToScan == null) {
        return;
    }
    if (! scanned.add(objectToScan)) { // to prevent any endless scan loops
        return;
    }
    // you might need some extra code if you want to correctly support scanning for primitive types
    if (lookingFor.isInstance(objectToScan)) {
        results.add(lookingFor.cast(objectToScan));
        // either return or continue to scan of target object might contains references to other objects of this type
    }
    // we won't find anything intresting in Strings, and it is pretty popular type
    if (objectToScan instanceof String) {
        return;
    }
    // basic support for popular java types to prevent scanning too much of java internals in most common cases, but might cause 
    // side-effects in some cases
    else if (objectToScan instanceof Iterable) {
        ((Iterable<?>) objectToScan).forEach(obj -> scanInstance(obj, lookingFor, scanned, results));
    }
    else if (objectToScan instanceof Map) {
        ((Map<?, ?>) objectToScan).forEach((key, value) -> {
            scanInstance(key, lookingFor, scanned, results);
            scanInstance(value, lookingFor, scanned, results);
        });
    }
    // remember about arrays, if you want to support primitive types remember to use Array class instead.
    else if (objectToScan instanceof Object[]) {
        int length = Array.getLength(objectToScan);
        for (int i = 0; i < length; i++) {
            scanInstance(Array.get(objectToScan, i), lookingFor, scanned, results);
        }
    }
    else if (objectToScan.getClass().isArray()) {
        return; // primitive array
    }
    else {
        Class<?> currentClass = objectToScan.getClass();
        while (currentClass != Object.class) {
            for (Field declaredField : currentClass.getDeclaredFields()) {
                // skip static fields
                if (Modifier.isStatic(declaredField.getModifiers())) {
                    continue;
                }
                // skip primitives, to prevent wrapping of "int" to "Integer" and then trying to scan its "value" field and loop endlessly.
                if (declaredField.getType().isPrimitive()) {
                    return;
                }
                if (! declaredField.trySetAccessible()) {
                    // either throw error, skip, or use more black magic like Unsafe class to make field accessible anyways.
                    continue; // I will just skip it, it's probably some internal one.
                }
                try {
                    scanInstance(declaredField.get(objectToScan), lookingFor, scanned, results);
                }
                catch (IllegalAccessException ignored) {
                    continue;
                }
            }
            currentClass = currentClass.getSuperclass();
        }
    }
}

将其拆分为几个方法是个好主意,但这只是概念证明,您仍应根据需要对其进行调整,但它适用于您的示例案例:

    List<X> instances = new ArrayList<>(); // either use list or set or identity set, depending if you want to see duplicates of data/instances
    scanInstance(myAClass, X.class, Collections.newSetFromMap(new IdentityHashMap<>()), instances);

    System.out.println(instances);

它会找到 6 个 X 类的实例。 (包括myAClass本身)

【讨论】:

  • 感谢您提供如此详细的答案。我的真正目的是通过反序列化 json 在 json 反序列化后自动装配 beans 和 Value 属性,然后调用 postProcess 方法来自动装配 beans:applicationContext.getAutowireCapableBeanFactory().autowireBeanProperties(clzz, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true)。因此,我需要找到所有可以包含 autowire 值的类,并且这些类必须是 X 类型。在这里我解释了我的真正问题:stackoverflow.com/questions/57753753/…
  • 您需要查找某些类的类或实例,也就是类或对象?
  • 但是如果这个答案符合你在这个问题中的要求,那么请接受它
【解决方案2】:

您用 [spring] 标记了您的问题,并在您的 cmets 中重申您正在使用 bean。使用 Spring,如果您想获得所有扩展基本类型的 bean 的集合,您可以这样做:

@Autowired
private List<? extends X> someCollection;

Spring 将使用具有基类 X 的所有匹配 bean 填充集合。

【讨论】:

    猜你喜欢
    • 2019-06-11
    • 1970-01-01
    • 1970-01-01
    • 2010-09-09
    • 2020-12-09
    • 1970-01-01
    • 2021-01-13
    • 2023-04-04
    • 2022-11-10
    相关资源
    最近更新 更多