是否有可能找到给定包中的所有类或接口?(快速看了一下e.g. Package,似乎没有。)


当前回答

如果您只是想加载一组相关的类,那么Spring可以帮助您。

Spring可以在一行代码中实例化实现给定接口的所有类的列表或映射。列表或映射将包含实现该接口的所有类的实例。

也就是说,作为从文件系统中加载类列表的替代方法,只需在您想要加载的所有类中实现相同的接口,而不管包是什么,并使用Spring为您提供所有这些类的实例。这样,您就可以加载(并实例化)您想要的所有类,而不管它们在哪个包中。

另一方面,如果您希望将它们都放在一个包中,那么只需让包中的所有类实现给定的接口。

注意,接口本身不需要声明任何方法——它可以完全为空。

要注入实现给定接口的类列表,请使用以下代码行…

  @Autowired
  private List<ISomeInterface> implementationList;

也可以使用Spring注入类的Map。如果有兴趣,请阅读文档。

最后,我将提供一个比搜索整个文件系统树更优雅的解决方案。

创建一个自定义注释,用于构建应用于它的类的目录——类似于@ClassCatalog。

其他回答

由于类装入器的动态特性,这是不可能的。类装入器不需要告诉VM它可以提供哪些类,相反,它们只是提交类请求,并且必须返回类或抛出异常。

但是,如果您编写自己的类装入器,或者检查类路径和它的jar,就有可能找到这些信息。不过,这将通过文件系统操作,而不是反射。甚至可能有一些库可以帮助你做到这一点。

如果有远程生成或交付的类,您将无法发现这些类。

通常的方法是在某个文件中注册需要访问的类,或者在不同的类中引用它们。或者在命名时使用惯例。

附录:反射库将允许您在当前类路径中查找类。它可以用来获取包中的所有类:

 Reflections reflections = new Reflections("my.project.prefix");

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

你好。我总是对上面的解决方案(以及其他网站上的)有一些问题。 我,作为一名开发人员,正在为API编程一个插件。该API防止使用任何外部库或第三方工具。设置还包括jar或zip文件中的代码和直接位于某些目录中的类文件。所以我的代码必须能够解决每一个设置。经过大量的研究,我想出了一个方法,可以在所有可能的设置中至少95%的情况下工作。

下面的代码基本上是总是有效的overkill方法。

代码:

这段代码扫描给定包中包含的所有类。它只对当前ClassLoader中的所有类有效。

/**
 * Private helper method
 * 
 * @param directory
 *            The directory to start with
 * @param pckgname
 *            The package name to search for. Will be needed for getting the
 *            Class object.
 * @param classes
 *            if a file isn't loaded but still is in the directory
 * @throws ClassNotFoundException
 */
private static void checkDirectory(File directory, String pckgname,
        ArrayList<Class<?>> classes) throws ClassNotFoundException {
    File tmpDirectory;

    if (directory.exists() && directory.isDirectory()) {
        final String[] files = directory.list();

        for (final String file : files) {
            if (file.endsWith(".class")) {
                try {
                    classes.add(Class.forName(pckgname + '.'
                            + file.substring(0, file.length() - 6)));
                } catch (final NoClassDefFoundError e) {
                    // do nothing. this class hasn't been found by the
                    // loader, and we don't care.
                }
            } else if ((tmpDirectory = new File(directory, file))
                    .isDirectory()) {
                checkDirectory(tmpDirectory, pckgname + "." + file, classes);
            }
        }
    }
}

/**
 * Private helper method.
 * 
 * @param connection
 *            the connection to the jar
 * @param pckgname
 *            the package name to search for
 * @param classes
 *            the current ArrayList of all classes. This method will simply
 *            add new classes.
 * @throws ClassNotFoundException
 *             if a file isn't loaded but still is in the jar file
 * @throws IOException
 *             if it can't correctly read from the jar file.
 */
private static void checkJarFile(JarURLConnection connection,
        String pckgname, ArrayList<Class<?>> classes)
        throws ClassNotFoundException, IOException {
    final JarFile jarFile = connection.getJarFile();
    final Enumeration<JarEntry> entries = jarFile.entries();
    String name;

    for (JarEntry jarEntry = null; entries.hasMoreElements()
            && ((jarEntry = entries.nextElement()) != null);) {
        name = jarEntry.getName();

        if (name.contains(".class")) {
            name = name.substring(0, name.length() - 6).replace('/', '.');

            if (name.contains(pckgname)) {
                classes.add(Class.forName(name));
            }
        }
    }
}

/**
 * Attempts to list all the classes in the specified package as determined
 * by the context class loader
 * 
 * @param pckgname
 *            the package name to search
 * @return a list of classes that exist within that package
 * @throws ClassNotFoundException
 *             if something went wrong
 */
public static ArrayList<Class<?>> getClassesForPackage(String pckgname)
        throws ClassNotFoundException {
    final ArrayList<Class<?>> classes = new ArrayList<Class<?>>();

    try {
        final ClassLoader cld = Thread.currentThread()
                .getContextClassLoader();

        if (cld == null)
            throw new ClassNotFoundException("Can't get class loader.");

        final Enumeration<URL> resources = cld.getResources(pckgname
                .replace('.', '/'));
        URLConnection connection;

        for (URL url = null; resources.hasMoreElements()
                && ((url = resources.nextElement()) != null);) {
            try {
                connection = url.openConnection();

                if (connection instanceof JarURLConnection) {
                    checkJarFile((JarURLConnection) connection, pckgname,
                            classes);
                } else if (connection instanceof FileURLConnection) {
                    try {
                        checkDirectory(
                                new File(URLDecoder.decode(url.getPath(),
                                        "UTF-8")), pckgname, classes);
                    } catch (final UnsupportedEncodingException ex) {
                        throw new ClassNotFoundException(
                                pckgname
                                        + " does not appear to be a valid package (Unsupported encoding)",
                                ex);
                    }
                } else
                    throw new ClassNotFoundException(pckgname + " ("
                            + url.getPath()
                            + ") does not appear to be a valid package");
            } catch (final IOException ioex) {
                throw new ClassNotFoundException(
                        "IOException was thrown when trying to get all resources for "
                                + pckgname, ioex);
            }
        }
    } catch (final NullPointerException ex) {
        throw new ClassNotFoundException(
                pckgname
                        + " does not appear to be a valid package (Null pointer exception)",
                ex);
    } catch (final IOException ioex) {
        throw new ClassNotFoundException(
                "IOException was thrown when trying to get all resources for "
                        + pckgname, ioex);
    }

    return classes;
}

这三个方法使您能够查找给定包中的所有类。 你可以这样使用它:

getClassesForPackage("package.your.classes.are.in");

解释:

该方法首先获取当前的ClassLoader。然后,它获取包含该包的所有资源,并迭代这些url。然后它创建一个URLConnection并确定我们拥有的URl类型。它可以是一个目录(FileURLConnection),也可以是一个jar或zip文件中的目录(JarURLConnection)。根据我们所拥有的连接类型,将调用两个不同的方法。

首先让我们看看如果它是一个FileURLConnection会发生什么。 它首先检查传递的File是否存在,是否为目录。如果是这样,它会检查它是否是类文件。如果是这样,一个Class对象将被创建并放入数组列表中。如果它不是一个类文件,而是一个目录,我们只需迭代它,并做同样的事情。所有其他案例/文件将被忽略。

如果URLConnection是JarURLConnection,另一个私有助手方法将被调用。此方法迭代zip/jar存档中的所有条目。如果一个条目是一个类文件,并且在包内部,一个class对象将被创建并存储在ArrayList中。

在所有资源被解析后,它(main方法)返回包含给定包中当前ClassLoader所知道的所有类的ArrayList。

如果进程在任何时候失败,将抛出一个ClassNotFoundException,其中包含关于确切原因的详细信息。

谷歌Guava 14包含了一个新类ClassPath,它有三个方法来扫描顶级类:

getTopLevelClasses () getTopLevelClasses(管柱packageName) getTopLevelClassesRecursive(管柱packageName)

有关更多信息,请参阅ClassPath javadocs。

如果您只是想加载一组相关的类,那么Spring可以帮助您。

Spring可以在一行代码中实例化实现给定接口的所有类的列表或映射。列表或映射将包含实现该接口的所有类的实例。

也就是说,作为从文件系统中加载类列表的替代方法,只需在您想要加载的所有类中实现相同的接口,而不管包是什么,并使用Spring为您提供所有这些类的实例。这样,您就可以加载(并实例化)您想要的所有类,而不管它们在哪个包中。

另一方面,如果您希望将它们都放在一个包中,那么只需让包中的所有类实现给定的接口。

注意,接口本身不需要声明任何方法——它可以完全为空。

要注入实现给定接口的类列表,请使用以下代码行…

  @Autowired
  private List<ISomeInterface> implementationList;

也可以使用Spring注入类的Map。如果有兴趣,请阅读文档。

最后,我将提供一个比搜索整个文件系统树更优雅的解决方案。

创建一个自定义注释,用于构建应用于它的类的目录——类似于@ClassCatalog。

目前列出给定包中所有类的最健壮的机制是ClassGraph,因为它处理尽可能广泛的类路径规范机制,包括新的JPMS模块系统。(我是作者。)

List<String> classNames = new ArrayList<>();
try (ScanResult scanResult = new ClassGraph().acceptPackages("my.package")
        .enableClassInfo().scan()) {
    classNames.addAll(scanResult.getAllClasses().getNames());
}