如何在类路径中读取Java包中的所有类?

时间:2009-09-21 21:18:16

标签: java reflection

我需要读取Java包中包含的类。这些类在classpath中。我需要直接从Java程序执行此任务。你知道一个简单的方法吗?

List<Class> classes = readClassesFrom("my.package")

17 个答案:

答案 0 :(得分:47)

如果您在类路径中有Spring,则以下内容将执行此操作。

查找使用XmlRootElement注释的包中的所有类:

private List<Class> findMyTypes(String basePackage) throws IOException, ClassNotFoundException
{
    ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
    MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);

    List<Class> candidates = new ArrayList<Class>();
    String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                               resolveBasePackage(basePackage) + "/" + "**/*.class";
    Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
    for (Resource resource : resources) {
        if (resource.isReadable()) {
            MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
            if (isCandidate(metadataReader)) {
                candidates.add(Class.forName(metadataReader.getClassMetadata().getClassName()));
            }
        }
    }
    return candidates;
}

private String resolveBasePackage(String basePackage) {
    return ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(basePackage));
}

private boolean isCandidate(MetadataReader metadataReader) throws ClassNotFoundException
{
    try {
        Class c = Class.forName(metadataReader.getClassMetadata().getClassName());
        if (c.getAnnotation(XmlRootElement.class) != null) {
            return true;
        }
    }
    catch(Throwable e){
    }
    return false;
}

答案 1 :(得分:29)

您可以使用描述here

的Reflections项目

它非常完整且易于使用。

上述网站的简要说明:

  

反射扫描您的类路径,索引元数据,允许您   在运行时查询它,可以为许多人保存和收集这些信息   项目中的模块。

示例:

Reflections reflections = new Reflections(
    new ConfigurationBuilder()
        .setUrls(ClasspathHelper.forJavaClassPath())
);
Set<Class<?>> types = reflections.getTypesAnnotatedWith(Scannable.class);

答案 2 :(得分:26)

我使用这个,它适用于文件或jar档案

public static ArrayList<String>getClassNamesFromPackage(String packageName) throws IOException{
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    URL packageURL;
    ArrayList<String> names = new ArrayList<String>();;

    packageName = packageName.replace(".", "/");
    packageURL = classLoader.getResource(packageName);

    if(packageURL.getProtocol().equals("jar")){
        String jarFileName;
        JarFile jf ;
        Enumeration<JarEntry> jarEntries;
        String entryName;

        // build jar file name, then loop through zipped entries
        jarFileName = URLDecoder.decode(packageURL.getFile(), "UTF-8");
        jarFileName = jarFileName.substring(5,jarFileName.indexOf("!"));
        System.out.println(">"+jarFileName);
        jf = new JarFile(jarFileName);
        jarEntries = jf.entries();
        while(jarEntries.hasMoreElements()){
            entryName = jarEntries.nextElement().getName();
            if(entryName.startsWith(packageName) && entryName.length()>packageName.length()+5){
                entryName = entryName.substring(packageName.length(),entryName.lastIndexOf('.'));
                names.add(entryName);
            }
        }

    // loop through files in classpath
    }else{
    URI uri = new URI(packageURL.toString());
    File folder = new File(uri.getPath());
        // won't work with path which contains blank (%20)
        // File folder = new File(packageURL.getFile()); 
        File[] contenuti = folder.listFiles();
        String entryName;
        for(File actual: contenuti){
            entryName = actual.getName();
            entryName = entryName.substring(0, entryName.lastIndexOf('.'));
            names.add(entryName);
        }
    }
    return names;
}

答案 3 :(得分:11)

Spring在PathMatchingResourcePatternResolver中实现了一个出色的类路径搜索功能。如果使用classpath*:前缀,则可以找到所有资源,包括给定层次结构中的类,甚至可以根据需要对其进行过滤。然后,您可以使用AbstractTypeHierarchyTraversingFilterAnnotationTypeFilterAssignableTypeFilter的子项在类级别注释或其实现的接口上过滤这些资源。

答案 4 :(得分:6)

Java 1.6.0_24:

public static File[] getPackageContent(String packageName) throws IOException{
    ArrayList<File> list = new ArrayList<File>();
    Enumeration<URL> urls = Thread.currentThread().getContextClassLoader()
                            .getResources(packageName);
    while (urls.hasMoreElements()) {
        URL url = urls.nextElement();
        File dir = new File(url.getFile());
        for (File f : dir.listFiles()) {
            list.add(f);
        }
    }
    return list.toArray(new File[]{});
}

此解决方案在EJB环境中进行了测试。

答案 5 :(得分:6)

ScannotationReflections使用类路径扫描方法:

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

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

Iterable<Class> classes = ClassIndex.getPackageClasses("my.package");

答案 6 :(得分:3)

据我所知,Java反射API仍然遗漏了这个功能。您可以通过以下方式获取包对象:

Package packageObj = Package.getPackage("my.package");

但是您可能已经注意到,这不会让您列出该包中的类。截至目前,您必须采取更加面向文件系统的方法。

我在this帖子

中找到了一些示例实现

当你的类被隐藏在JAR文件中时,我并不是100%确定这些方法是可行的,但我希望其中一个能为你做到这一点。

我同意@skaffman ......如果你有另一种解决方法,我建议你这样做。

答案 7 :(得分:3)

你可以试试我的图书馆FastClasspathScanner。扫描类路径并不像检查java.class.path属性那样简单,并且在那里递归扫描类,因为可以通过多种方式指定类路径(例如,您可以将Class-Path条目添加到jar文件中&# 39; s清单)。使用Class.forName()实际上初始化类,这可能不是你想要的,等等.FastClasspathScanner为你处理这些复杂性。

要枚举包中的所有类,请执行以下操作:

String packageName = "my.package";
Set<String> classNames = new FastClassPathScanner(packageName)
    .scan()
    .getNamesOfAllClasses();
String packageNamePrefix = packageName + ".";
for (String className : classNames) {
    if (className.startsWith(packageNamePrefix) {
        System.out.println("Found class: " + className);
   }
}

您需要检查if (className.startsWith(packageNamePrefix),因为如果某个类引用另一个类(例如,作为超类),并且它位于列入白名单的包前缀my.package之外,则它将包含在由.getNamesOfAllClasses()返回的设置。

答案 8 :(得分:2)

  1. Bill Burke写了一篇关于班级扫描的好article,然后写了 Scannotation

  2. Hibernate 已经写了这个:

    • org.hibernate.ejb.packaging.Scanner
    • org.hibernate.ejb.packaging.NativeScanner
  3. CDI可以解决这个问题,但不知道 - 尚未完全调查

  4. @Inject Instance< MyClass> x;
    ...
    x.iterator() 
    

    同样适用于注释:

    abstract class MyAnnotationQualifier
    extends AnnotationLiteral<Entity> implements Entity {}
    

答案 9 :(得分:2)

eXtcos看起来很有希望。想象一下,你想要找到所有类:

  1. 从“Component”类扩展,并存储它们
  2. 使用“MyComponent”和
  3. 进行注释
  4. 属于“普通”套餐。
  5. 使用eXtcos,这就像

    一样简单
    ClasspathScanner scanner = new ClasspathScanner();
    final Set<Class> classStore = new ArraySet<Class>();
    
    Set<Class> classes = scanner.getClasses(new ClassQuery() {
        protected void query() {
            select().
            from(“common”).
            andStore(thoseExtending(Component.class).into(classStore)).
            returning(allAnnotatedWith(MyComponent.class));
        }
    });
    

答案 10 :(得分:1)

这是另一种选择,稍微修改上面/下面的另一个答案:

Reflections reflections = new Reflections("com.example.project.package", 
    new SubTypesScanner(false));
Set<Class<? extends Object>> allClasses = 
    reflections.getSubTypesOf(Object.class);

答案 11 :(得分:1)

我碰巧实现了它,它在大多数情况下都有效。由于它很长,我把它放在file here

这个想法是找到在大多数情况下可用的类源文件的位置(已知的例外是JVM类文件 - 据我测试过)。如果代码在目录中,则扫描所有文件并仅查找类文件。如果代码在JAR文件中,请扫描所有条目。

此方法只能在以下情况下使用:

  1. 您有一个类想要发现在同一个包中,该类称为SeedClass。例如,如果要列出“java.io”中的所有类,则种子类可能为java.io.File

  2. 您的类位于目录或JAR文件中,它具有源文件信息(不是源代码文件,而只是源文件)。据我所知,除了JVM类(这些类附带JVM)之外,它几乎可以100%工作。

  3. 您的程序必须具有访问这些类的ProtectionDomain的权限。如果您的程序是在本地加载的,那么应该没有问题。

  4. 我测试的程序仅用于我的常规用法,所以它可能仍有问题。

    我希望这会有所帮助。

答案 12 :(得分:0)

如果您已经在使用Guava,则可以使用ClassPath。  在这里查看文档:  https://google.github.io/guava/releases/17.0/api/docs/com/google/common/reflect/ClassPath.html

答案 13 :(得分:0)

使用依赖maven:

groupId: net.sf.extcos
artifactId: extcos
version: 0.4b

然后使用此代码:

ComponentScanner scanner = new ComponentScanner();
        Set classes = scanner.getClasses(new ComponentQuery() {
            @Override
            protected void query() {
                select().from("com.leyton").returning(allExtending(DynamicForm.class));
            }
        });

答案 14 :(得分:0)

项目ldapbeans提供了执行此操作的课程Scanner

答案 15 :(得分:0)

当applet很常见时,可能会在类路径上有一个URL。当类加载器需要一个类时,它将搜索类路径上的所有位置,包括http资源。因为你可以在类路径中包含URL和目录之类的东西,所以没有简单的方法来获得类的明确列表。

然而,你可以非常接近。一些Spring库现在正在这样做。您可以在类路径中获取所有jar,并像文件一样打开它们。然后,您可以获取此文件列表,并创建包含您的类的数据结构。

答案 16 :(得分:-2)

Brent - 关联是一种方式的原因与CLASSPATH的任何组件上的任何类都可以在任何包中声明自身(java / javax除外)这一事实有关。因此,在给定的“包”中没有所有类的映射,因为没有人知道也不知道。您可以明天更新jar文件并删除或添加类。这就像试图获得世界上所有国家中名为John / Jon / Johan的所有人的名单 - 我们都不是无所不知的,因此我们中没有人会得到正确答案。