【问题标题】:Find all classes in package (& call static methods) at runtime在运行时查找包中的所有类(并调用静态方法)
【发布时间】:2020-02-22 15:19:40
【问题描述】:

我有一个包含许多类的包,这些类都有一个名为onLoad 的静态方法。我想在程序启动时调用onLoad 方法,但是我不想硬连线每一个,即classA.onLoad(); classB.onLoad(); 等。

如何列出包com.foo.bar.asd 中的所有类并在所有类上运行onLoad()

提前致谢

【问题讨论】:

  • 对不起,我把标题搞砸了。它说我想创建我不想创建的这些类的实例。我想在其中调用静态方法,并且我已经编辑了标题。
  • 核心反射不提供列出包中所有类的方法。但是,有一些库可以帮助解决这个问题,其中许多都展示了 here。从那里您可能会拥有Class 对象或类名。如果你最终得到一个类名,那么你可以get the Class object using reflection。拥有Class 对象后,您只需have to find and invoke the static method

标签: java class package


【解决方案1】:

我发现这个问题很有趣,所以我想出了一个解决方案。这里的棘手部分是实际找到给定包中的所有类。

在这个例子中,我假设所有类都在类 C 所在的同一个包中,并且除了 C 之外的所有类都有一个可能无法访问的 onLoad 方法(即 private)。然后就可以使用下面的示例代码了。

public final class C {

    public static void main(String[] args) throws Exception {
        for (Class<?> cls : getClasses(C.class)) {
            if (cls != C.class) {
                Method onLoad = cls.getDeclaredMethod("onLoad");
                onLoad.setAccessible(true);
                onLoad.invoke(null);
            }
        }
    }

    private static List<Class<?>> getClasses(Class<?> caller)
            throws IOException, URISyntaxException {
        return Files.walk(getPackagePath(caller))
                .filter(Files::isRegularFile)
                .filter(file -> file.toString().endsWith(".class"))
                .map(path -> mapPathToClass(path, caller.getPackage().getName()))
                .collect(Collectors.toList());
    }

    private static Class<?> mapPathToClass(Path clsPath, String packageName) {
        String className = clsPath.toFile().getName();
        className = className.substring(0, className.length() - 6);
        return loadClass(packageName + "." + className);
    }

    private static Path getPackagePath(Class<?> caller)
            throws IOException, URISyntaxException {
        String packageName = createPackageName(caller);
        Enumeration<URL> resources = caller.getClassLoader()
                .getResources(packageName);
        return Paths.get(resources.nextElement().toURI());
    }

    private static String createPackageName(Class<?> caller) {
        return caller.getPackage().getName().replace(".", "/");
    }

    private static Class<?> loadClass(String name) {
        try {
            return Class.forName(name);
        } catch (ClassNotFoundException e) {
            return null;
        }
    }
}

我省略了所有异常检查,像resources.nextElement() 这样的语句甚至可能引发异常。所以如果你想覆盖这些情况,你需要在这里和那里添加一些检查。

【讨论】:

  • 有时间我会试试这个。有没有办法让类在不同的包中?不是真的需要,但它使源代码树有点整洁
  • @Jachdich 当然,在 main 方法中,将包内并具有 onLoad 方法的类之一传递给 getClasses(而不是 C.class)。然后,您也可以安全地删除 if 语句。
  • 酷,我明天试试。谢谢!
  • 抱歉昨天没时间。我今晚试试
  • 是的,它确实有效,谢谢!但是,当我尝试导出为 JAR 时,它会因“FileSystemNotFoundException”而失败。完整的日志可以找到here
【解决方案2】:

您可以使用reflections 库。

...
import org.reflections.Reflections;
import org.reflections.scanners.ResourcesScanner;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;

public static void main(String[] args)
   throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException 
{
  Reflections reflections = new Reflections(new ConfigurationBuilder()
    .setScanners(new SubTypesScanner(false /* don't exclude Object.class */), new ResourcesScanner())
    .setUrls(ClasspathHelper.forClassLoader(ClasspathHelper.contextClassLoader()))
    .filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix("com.foo.bar.asd"))));

  Set<Class<?>> classes = reflections.getSubTypesOf(Object.class);


  for (Class<?> clazz : classes)
  {
     Method method = clazz.getMethod("onLoad");
     method.invoke(null);
  }
}

请注意,这个库也有依赖项(guava.jar 和 javassist.jar)。

【讨论】:

  • 我真的很想远离番石榴,因为它是谷歌,但其他人无疑会觉得这很有用
猜你喜欢
  • 2013-06-04
  • 2021-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-13
  • 2011-01-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多