你能用反射找到包中的所有类吗?

Jon*_*nik 504 java reflection packages

是否可以找到给定包中的所有类或接口?(快速查看例如Package,它似乎没有.)

Sta*_*ale 359

由于类加载器的动态特性,这是不可能的.类加载器不需要告诉VM它可以提供哪些类,而是只需要传递类的请求,并且必须返回类或抛出异常.

但是,如果您编写自己的类加载器,或检查类路径及其罐子,则可以找到此信息.这将通过文件系统操作,而不是反射.甚至可能有图书馆可以帮助你做到这一点.

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

相反,常规方法是在某个地方注册您需要在文件中访问的类,或者在不同的类中引用它们.或者只是在命名时使用约定.

附录:Reflections库将允许您在当前类路径中查找类.它可用于获取包中的所有类:

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

 Set<Class<? extends Object>> allClasses = 
     reflections.getSubTypesOf(Object.class);
Run Code Online (Sandbox Code Playgroud)

  • 如果这返回一个空列表,则像这样初始化Reflections对象:Reflections reflections = new Reflections("your.package.here",new SubTypesScanner(false)); (64认同)
  • 请注意,此解决方案将无法正常工作,因为默认情况下,getSubTypesOf不会返回Object的子类型.有关如何配置SubTypeScanner的信息,请参阅AleksanderBlomskøld的解决方案. (16认同)
  • 反思需要番石榴.番石榴很大.版本14.0.1是2.1MB. (15认同)
  • 无法查询类名已经困扰了我很长一段时间.当然,它很难并且性能可以变化很大,对于某些类加载器,列表是不确定的或无限制的,但是有一些方法可以解决这个问题. (12认同)
  • 不适合我.Mac OSX - 思考依赖版本0.9.9-RC1(maven) - JDK 1.7.重新考虑接受的答案.@AleksanderBlomskøld回答是最好的.!!!!! (3认同)
  • FastClasspathScanner 是一个优秀的库,经过深思熟虑和记录。它也很快。 (2认同)

Ale*_*øld 180

你应该看一下开源的Reflections库.有了它,您可以轻松实现您想要的.

首先,设置反射索引(因为默认情况下禁用搜索所有类,所以它有点乱):

List<ClassLoader> classLoadersList = new LinkedList<ClassLoader>();
classLoadersList.add(ClasspathHelper.contextClassLoader());
classLoadersList.add(ClasspathHelper.staticClassLoader());

Reflections reflections = new Reflections(new ConfigurationBuilder()
    .setScanners(new SubTypesScanner(false /* don't exclude Object.class */), new ResourcesScanner())
    .setUrls(ClasspathHelper.forClassLoader(classLoadersList.toArray(new ClassLoader[0])))
    .filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix("org.your.package"))));
Run Code Online (Sandbox Code Playgroud)

然后,您可以查询给定包中的所有对象:

Set<Class<?>> classes = reflections.getSubTypesOf(Object.class);
Run Code Online (Sandbox Code Playgroud)

  • 啊,我们走了:http://code.google.com/p/reflections/issues/detail?id = 122.默认情况下会排除对象,但您可以重新调整它.谢谢你把我指向这个图书馆,真是太棒了! (6认同)
  • 如果有人想知道获取默认包的最简单方法是使用前缀为空字符串 - >"". (3认同)
  • Serge,我认为你误解了WTFPL:http://www.wtfpl.net/我认为WTFPL意味着你可以自由地做任何你想要的事情,不仅仅是许可证,而是代码还是 (3认同)
  • "Reflections"库有一个棘手的许可证:https://github.com/ronmamo/reflections/blob/master/COPYING.txt.诀窍是许可证允许仅免费使用许可证本身.因此,要真正使用库(而不是许可证),每个人都必须联系作者并协商使用条款. (2认同)

Chr*_*ter 116

Google Guava 14包含一个新类,ClassPath其中包含三种扫描顶级类的方法:

  • getTopLevelClasses()
  • getTopLevelClasses(String packageName)
  • getTopLevelClassesRecursive(String packageName)

有关更多信息,请参阅ClassPathjavadocs.

  • 我认为他的意思是在另一个答案中提到的Reflections库. (5认同)
  • 看来,ClassPath在Android上不起作用. (3认同)

小智 105

您可以使用此方法1使用ClassLoader.

/**
 * Scans all classes accessible from the context class loader which belong to the given package and subpackages.
 *
 * @param packageName The base package
 * @return The classes
 * @throws ClassNotFoundException
 * @throws IOException
 */
private static Class[] getClasses(String packageName)
        throws ClassNotFoundException, IOException {
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    assert classLoader != null;
    String path = packageName.replace('.', '/');
    Enumeration<URL> resources = classLoader.getResources(path);
    List<File> dirs = new ArrayList<File>();
    while (resources.hasMoreElements()) {
        URL resource = resources.nextElement();
        dirs.add(new File(resource.getFile()));
    }
    ArrayList<Class> classes = new ArrayList<Class>();
    for (File directory : dirs) {
        classes.addAll(findClasses(directory, packageName));
    }
    return classes.toArray(new Class[classes.size()]);
}

/**
 * Recursive method used to find all classes in a given directory and subdirs.
 *
 * @param directory   The base directory
 * @param packageName The package name for classes found inside the base directory
 * @return The classes
 * @throws ClassNotFoundException
 */
private static List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException {
    List<Class> classes = new ArrayList<Class>();
    if (!directory.exists()) {
        return classes;
    }
    File[] files = directory.listFiles();
    for (File file : files) {
        if (file.isDirectory()) {
            assert !file.getName().contains(".");
            classes.addAll(findClasses(file, packageName + "." + file.getName()));
        } else if (file.getName().endsWith(".class")) {
            classes.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6)));
        }
    }
    return classes;
}
Run Code Online (Sandbox Code Playgroud)

__________
1此方法最初来自http://snippets.dzone.com/posts/show/4831,该文件由Internet Archive存档,与现在相关联.该片段也可在https://dzone.com/articles/get-all-classes-within-package上找到.

  • 如果类在Jar内部,这也不起作用. (21认同)
  • 您刚刚从https://dzone.com/articles/get-all-classes-within-package复制了!请在下次参考来源 (18认同)
  • +1,因为这个解决方案不需要外部库......从来没有,真的永远不会将你的代码随机地与库耦合,只是为了实现这样的小事.你知道你为攻击者增加了潜在的攻击面吗?2015年11月发现一个Apache Commons问题导致远程命令执行,只需在Jboss/Weblogic上部署的应用程序的类路径中使用Apache Commons [http://foxglovesecurity.com/2015/11/06/what-do-weblogic- WebSphere的的jboss-詹金斯-OpenNMS的,和你的应用程序,有合共此-漏洞/] (16认同)
  • 如果我的路径包含空格,我遇到了问题.URL类将空格转义为`%20`,但是`new File()`构造函数将其视为文字百分号,为零.我通过将`dirs.add(...)`行更改为此来修复它:`dirs.add(new File(resource.toURI()));`这也意味着我必须将'URISyntaxException`添加到throws `getClasses`的子句 (6认同)
  • 我无法让它发挥作用。我们能否获得一个有效的包名称输入示例以及示例项目结构? (2认同)

voh*_*oho 90

弹簧

此示例适用于Spring 4,但您也可以在早期版本中找到类路径扫描程序.

// create scanner and disable default filters (that is the 'false' argument)
final ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
// add include filters which matches all the classes (or use your own)
provider.addIncludeFilter(new RegexPatternTypeFilter(Pattern.compile(".*")));

// get matching classes defined in the package
final Set<BeanDefinition> classes = provider.findCandidateComponents("my.package.name");

// this is how you can load the class type from BeanDefinition instance
for (BeanDefinition bean: classes) {
    Class<?> clazz = Class.forName(bean.getBeanClassName());
    // ... do your magic with the class ...
}
Run Code Online (Sandbox Code Playgroud)

谷歌番石榴

注意:在版本14中,API仍标记为@Beta,因此请注意生产代码.

final ClassLoader loader = Thread.currentThread().getContextClassLoader();

for (final ClassPath.ClassInfo info : ClassPath.from(loader).getTopLevelClasses()) {
  if (info.getName().startsWith("my.package.")) {
    final Class<?> clazz = info.load();
    // do something with your clazz
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 很好的答案.这里有太多的解决方案,这些解决方案冗长,未经过测试,无法正常工作!这个很棒:它简洁而经过测试(来自番石榴).很好!这很有用,值得更多的赞成. (5认同)

Bra*_*one 38

你好.我总是遇到上述解决方案(以及其他网站)的一些问题.
作为开发人员,我正在为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;
}
Run Code Online (Sandbox Code Playgroud)

这三种方法使您能够查找给定包中的所有类.
你这样使用它:

getClassesForPackage("package.your.classes.are.in");
Run Code Online (Sandbox Code Playgroud)

说明:

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

首先让我们看看如果它是一个会发生什么FileURLConnection.
它首先检查传递的文件是否存在并且是否是目录.如果是这种情况,则检查它是否是类文件.如果是这样,Class将创建一个对象并将其放入ArrayList.如果它不是类文件而是目录,我们只需迭代它并执行相同的操作.所有其他案例/文件将被忽略.

如果URLConnectionJarURLConnection另一个私有帮助方法将被调用.此方法迭代zip/jar存档中的所有条目.如果一个条目是类文件并且在包内,Class则将创建一个对象并将其存储在ArrayList.

解析完所有资源后(main方法)返回ArrayList给定包中所有类的包含,即当前ClassLoader所知的类.

如果该过程在任何时候ClassNotFoundException都失败,则会抛出有关确切原因的详细信息.

  • 此示例似乎需要导入`sun.net.www.protocol.file.FileURLConnection`,它在编译时生成警告("警告:sun.net.www.protocol.file.FileURLConnection是Sun专有API,可能是在将来的版本中删除").是否有使用该类的替代方法,或者可以使用注释来抑制警告? (4认同)

小智 15

不使用任何额外的库:

package test;

import java.io.DataInputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

public class Test {
    public static void main(String[] args) throws Exception{
        List<Class> classes = getClasses(Test.class.getClassLoader(),"test");
        for(Class c:classes){
            System.out.println("Class: "+c);
        }
    }

    public static List<Class> getClasses(ClassLoader cl,String pack) throws Exception{

        String dottedPackage = pack.replaceAll("[/]", ".");
        List<Class> classes = new ArrayList<Class>();
        URL upackage = cl.getResource(pack);

        DataInputStream dis = new DataInputStream((InputStream) upackage.getContent());
        String line = null;
        while ((line = dis.readLine()) != null) {
            if(line.endsWith(".class")) {
               classes.add(Class.forName(dottedPackage+"."+line.substring(0,line.lastIndexOf('.'))));
            }
        }
        return classes;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 使用此代码时,我得到一个null.似乎只有你的jar是可执行文件才能工作 (4认同)
  • 如果你从`String pack = getPackage().getName();` 得到包名,那么你必须加上`pack = pack.replaceAll("[.]", "/");` (2认同)

Sła*_*wek 13

通常,类加载器不允许扫描类路径上的所有类.但通常唯一使用的类加载器是UrlClassLoader,我们可以从中检索目录和jar文件列表(请参阅getURL)并逐个打开它们以列出可用的类.这种方法称为类路径扫描,在ScannotationReflections中实现.

Reflections reflections = new Reflections("my.package");
Set<Class<? extends Object>> classes = reflections.getSubTypesOf(Object.class);
Run Code Online (Sandbox Code Playgroud)

另一种方法是使用Java Pluggable Annotation Processing API编写注释处理器,该处理器将在编译时收集所有带注释的类,并构建索引文件以供运行时使用.此机制在ClassIndex库中实现:

// package-info.java
@IndexSubclasses
package my.package;

// your code
Iterable<Class> classes = ClassIndex.getPackageClasses("my.package");
Run Code Online (Sandbox Code Playgroud)

请注意,由于Java编译器会自动发现类路径中的任何处理器,因此扫描完全自动化不需要额外的设置.


Luk*_*son 8

我写了FastClasspathScanner来解决这个问题.它处理许多不同类型的类路径扫描任务,具有简单的API,适用于许多不同的ClassLoader和类路径环境,经过精心并行化,并针对高速和低内存消耗进行了高度优化.它甚至可以生成类图的GraphViz可视化,显示类如何相互连接.

对于查找给定包中所有类或接口的原始问题,您可以执行以下操作:

List<String> classNames = new ArrayList<>();
try (ScanResult scanResult = new ClassGraph().whitelistPackages("my.package")
        .enableClassInfo().scan()) {
    classNames.addAll(scanResult.getAllClasses().getNames());
}
Run Code Online (Sandbox Code Playgroud)

有许多可能的变体 - 请参阅文档(上面链接)以获取完整信息.


tir*_*irz 7

那这个呢:

public static List<Class<?>> getClassesForPackage(final String pkgName) throws IOException, URISyntaxException {
    final String pkgPath = pkgName.replace('.', '/');
    final URI pkg = Objects.requireNonNull(ClassLoader.getSystemClassLoader().getResource(pkgPath)).toURI();
    final ArrayList<Class<?>> allClasses = new ArrayList<Class<?>>();

    Path root;
    if (pkg.toString().startsWith("jar:")) {
        try {
            root = FileSystems.getFileSystem(pkg).getPath(pkgPath);
        } catch (final FileSystemNotFoundException e) {
            root = FileSystems.newFileSystem(pkg, Collections.emptyMap()).getPath(pkgPath);
        }
    } else {
        root = Paths.get(pkg);
    }

    final String extension = ".class";
    try (final Stream<Path> allPaths = Files.walk(root)) {
        allPaths.filter(Files::isRegularFile).forEach(file -> {
            try {
                final String path = file.toString().replace('/', '.');
                final String name = path.substring(path.indexOf(pkgName), path.length() - extension.length());
                allClasses.add(Class.forName(name));
            } catch (final ClassNotFoundException | StringIndexOutOfBoundsException ignored) {
            }
        });
    }
    return allClasses;
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以重载该函数:

public static List<Class<?>> getClassesForPackage(final Package pkg) throws IOException, URISyntaxException {
    return getClassesForPackage(pkg.getName());
}
Run Code Online (Sandbox Code Playgroud)

如果你需要测试它:

public static void main(final String[] argv) throws IOException, URISyntaxException {
    for (final Class<?> cls : getClassesForPackage("my.package")) {
        System.out.println(cls);
    }
    for (final Class<?> cls : getClassesForPackage(MyClass.class.getPackage())) {
        System.out.println(cls);
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您的 IDE 没有导入助手:

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.FileSystemNotFoundException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
Run Code Online (Sandbox Code Playgroud)

有用:

  • 从你的 IDE

  • 对于 JAR 文件

  • 没有外部依赖

  • 要在 Windows 上工作,您必须将 file.toString().replace('/', '.') 更改为 Final String path = file.toString().replace('\\, '.'); (3认同)

Dav*_*son 5

我整理了一个简单的github项目来解决这个问题:

https://github.com/ddopson/java-class-enumerator

它应该适用于基于文件的类路径和 jar 文件。

如果您在签出项目后运行“make”,它将打印出来:

 Cleaning...
rm -rf build/
 Building...
javac -d build/classes src/pro/ddopson/ClassEnumerator.java src/test/ClassIShouldFindOne.java src/test/ClassIShouldFindTwo.java src/test/subpkg/ClassIShouldFindThree.java src/test/TestClassEnumeration.java
 Making JAR Files...
jar cf build/ClassEnumerator_test.jar -C build/classes/ . 
jar cf build/ClassEnumerator.jar -C build/classes/ pro
 Running Filesystem Classpath Test...
java -classpath build/classes test.TestClassEnumeration
ClassDiscovery: Package: 'test' becomes Resource: 'file:/Users/Dopson/work/other/java-class-enumeration/build/classes/test'
ClassDiscovery: Reading Directory '/Users/Dopson/work/other/java-class-enumeration/build/classes/test'
ClassDiscovery: FileName 'ClassIShouldFindOne.class'  =>  class 'test.ClassIShouldFindOne'
ClassDiscovery: FileName 'ClassIShouldFindTwo.class'  =>  class 'test.ClassIShouldFindTwo'
ClassDiscovery: FileName 'subpkg'  =>  class 'null'
ClassDiscovery: Reading Directory '/Users/Dopson/work/other/java-class-enumeration/build/classes/test/subpkg'
ClassDiscovery: FileName 'ClassIShouldFindThree.class'  =>  class 'test.subpkg.ClassIShouldFindThree'
ClassDiscovery: FileName 'TestClassEnumeration.class'  =>  class 'test.TestClassEnumeration'
 Running JAR Classpath Test...
java -classpath build/ClassEnumerator_test.jar  test.TestClassEnumeration
ClassDiscovery: Package: 'test' becomes Resource: 'jar:file:/Users/Dopson/work/other/java-class-enumeration/build/ClassEnumerator_test.jar!/test'
ClassDiscovery: Reading JAR file: '/Users/Dopson/work/other/java-class-enumeration/build/ClassEnumerator_test.jar'
ClassDiscovery: JarEntry 'META-INF/'  =>  class 'null'
ClassDiscovery: JarEntry 'META-INF/MANIFEST.MF'  =>  class 'null'
ClassDiscovery: JarEntry 'pro/'  =>  class 'null'
ClassDiscovery: JarEntry 'pro/ddopson/'  =>  class 'null'
ClassDiscovery: JarEntry 'pro/ddopson/ClassEnumerator.class'  =>  class 'null'
ClassDiscovery: JarEntry 'test/'  =>  class 'null'
ClassDiscovery: JarEntry 'test/ClassIShouldFindOne.class'  =>  class 'test.ClassIShouldFindOne'
ClassDiscovery: JarEntry 'test/ClassIShouldFindTwo.class'  =>  class 'test.ClassIShouldFindTwo'
ClassDiscovery: JarEntry 'test/subpkg/'  =>  class 'null'
ClassDiscovery: JarEntry 'test/subpkg/ClassIShouldFindThree.class'  =>  class 'test.subpkg.ClassIShouldFindThree'
ClassDiscovery: JarEntry 'test/TestClassEnumeration.class'  =>  class 'test.TestClassEnumeration'
 Tests Passed. 
Run Code Online (Sandbox Code Playgroud)

另见我的另一个答案


Nad*_*v B 5

这是我如何做到的.我扫描所有子文件夹(子包),我不尝试加载匿名类:

   /**
   * Attempts to list all the classes in the specified package as determined
   * by the context class loader, recursively, avoiding anonymous classes
   * 
   * @param pckgname
   *            the package name to search
   * @return a list of classes that exist within that package
   * @throws ClassNotFoundException
   *             if something went wrong
   */
  private static List<Class> getClassesForPackage(String pckgname) throws ClassNotFoundException {
      // This will hold a list of directories matching the pckgname. There may be more than one if a package is split over multiple jars/paths
      ArrayList<File> directories = new ArrayList<File>();
      String packageToPath = pckgname.replace('.', '/');
      try {
          ClassLoader cld = Thread.currentThread().getContextClassLoader();
          if (cld == null) {
              throw new ClassNotFoundException("Can't get class loader.");
          }

          // Ask for all resources for the packageToPath
          Enumeration<URL> resources = cld.getResources(packageToPath);
          while (resources.hasMoreElements()) {
              directories.add(new File(URLDecoder.decode(resources.nextElement().getPath(), "UTF-8")));
          }
      } catch (NullPointerException x) {
          throw new ClassNotFoundException(pckgname + " does not appear to be a valid package (Null pointer exception)");
      } catch (UnsupportedEncodingException encex) {
          throw new ClassNotFoundException(pckgname + " does not appear to be a valid package (Unsupported encoding)");
      } catch (IOException ioex) {
          throw new ClassNotFoundException("IOException was thrown when trying to get all resources for " + pckgname);
      }

      ArrayList<Class> classes = new ArrayList<Class>();
      // For every directoryFile identified capture all the .class files
      while (!directories.isEmpty()){
          File directoryFile  = directories.remove(0);             
          if (directoryFile.exists()) {
              // Get the list of the files contained in the package
              File[] files = directoryFile.listFiles();

              for (File file : files) {
                  // we are only interested in .class files
                  if ((file.getName().endsWith(".class")) && (!file.getName().contains("$"))) {
                      // removes the .class extension
                      int index = directoryFile.getPath().indexOf(packageToPath);
                      String packagePrefix = directoryFile.getPath().substring(index).replace('/', '.');;                          
                    try {                  
                      String className = packagePrefix + '.' + file.getName().substring(0, file.getName().length() - 6);                            
                      classes.add(Class.forName(className));                                
                    } catch (NoClassDefFoundError e)
                    {
                      // do nothing. this class hasn't been found by the loader, and we don't care.
                    }
                  } else if (file.isDirectory()){ // If we got to a subdirectory
                      directories.add(new File(file.getPath()));                          
                  }
              }
          } else {
              throw new ClassNotFoundException(pckgname + " (" + directoryFile.getPath() + ") does not appear to be a valid package");
          }
      }
      return classes;
  }  
Run Code Online (Sandbox Code Playgroud)


Suj*_*dal 5

是的,您可以使用很少的 API,这就是我喜欢这样做的方式,遇到了我使用 hibernate 核心时遇到的这个问题,并且必须找到用特定注释进行注释的类。

使这些成为自定义注释,您将使用它来标记要选择的类。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface EntityToBeScanned {

}
Run Code Online (Sandbox Code Playgroud)

然后用它标记你的班级

@EntityToBeScanned 
public MyClass{

}
Run Code Online (Sandbox Code Playgroud)

使这个实用程序类具有以下方法

public class ClassScanner {

    public static Set<Class<?>> allFoundClassesAnnotatedWithEntityToBeScanned(){
        Reflections reflections = new Reflections(".*");
        Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(EntityToBeScanned.class);
        return annotated;
    }

}
Run Code Online (Sandbox Code Playgroud)

调用allFoundClassesAnnotatedWithEntityToBeScanned()方法以获取找到的一类。

您将需要下面给出的库

<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>21.0</version>
    </dependency>
<!-- https://mvnrepository.com/artifact/org.javassist/javassist -->
<dependency>
    <groupId>org.javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.22.0-CR1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.reflections/reflections -->
<dependency>
    <groupId>org.reflections</groupId>
    <artifactId>reflections</artifactId>
    <version>0.9.10</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

361795 次

最近记录:

5 年,11 月 前