如何从类path中的Java包中读取所有类?

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

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

如果你在classpath中有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; } 

你可以使用这里描述的reflection项目

这是相当完整和易于使用。

以上网站简要说明:

reflection扫描您的类path,索引元数据,允许您在运行时查询它,并可以保存和收集项目中许多模块的信息。

例:

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

我使用这个,它与文件或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; } 

Spring在PathMatchingResourcePatternResolver实现了极好的类pathsearchfunction。 如果使用classpath* :前缀,则可以find所有资源(包括给定层次结构中的类),甚至可以根据需要对其进行过滤。 然后,您可以使用AbstractTypeHierarchyTraversingFilterAnnotationTypeFilterAssignableTypeFilter的子级在类级别注释或其实现的接口上过滤这些资源。

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[]{}); } 

该解决scheme在EJB环境中进行了testing。

Scannotation和Reflections使用类path扫描方法:

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

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

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

就我所知,JavareflectionAPI中仍然缺less这个function。 你可以通过这样做来获得一个包对象:

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

但是,正如你可能注意到的,这不会让你列出该包中的类。 到目前为止,你必须采取更多的面向文件系统的方法。

我在这篇文章中发现了一些示例实现

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

我同意@skaffman …如果你有另一种方式去做这件事,我build议你这样做。

我恰巧已经实现了它,并且在大多数情况下都可以工作。 由于时间长了,我把它放在一个文件里 。

我们的想法是find在大多数情况下可用的类源文件的位置(一个已知的例外是JVM类文件 – 据我testing)。 如果代码位于目录中,则扫描所有文件并仅查找类文件。 如果代码位于JAR文件中,则扫描所有条目。

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

  1. 你有一个你想要发现的包中的类,这个类叫做SeedClass。 例如,如果要列出“java.io”中的所有类,则种子类可以是java.io.File

  2. 你的类是在一个目录或JAR文件中,它有源文件信息(不是源代码文件,而只是源文件)。 据我所知,除了JVM类(这些类与JVM一起)之外,它几乎可以工作100%。

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

我testing过的程序只用于我的日常使用,所以它可能仍然有问题。

我希望这有帮助。

eXtcos看起来很有前景。 想象一下,你想find所有的类:

  1. 从“组件”类扩展并存储它们
  2. 用“MyComponent”注释
  3. 是在“共同”的包。

有了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)); } }); 
  1. 比尔·伯克(Bill Burke)写了一篇关于课堂扫描的好文章 ,然后写了Scannotation

  2. Hibernate已经写了这个:

    • org.hibernate.ejb.packaging.Scanner
    • org.hibernate.ejb.packaging.NativeScanner
  3. CDI可能会解决这个问题,但不知道 – 还没有完全调查

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

也用于注释:

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

你可以试试我的库FastClasspathScanner 。 扫描类path并不像检查java.class.path属性那么简单,而是recursion地扫描那里的类,因为可以指定类path的许多方法(例如,可以将Class-Path条目添加到jar文件的清单中)。 使用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()

当小程序是普通的地方时,可能会在类path中有一个URL。 当类加载器需要一个类时,它将search类path上的所有位置,包括http资源。 因为您可以在类path中包含URL和目录之类的东西,所以没有简单的方法可以获取最终的类列表。

但是,你可以相当接近。 一些Spring库现在正在这样做。 你可以在类path上获得所有的jar,并像文件一样打开它们。 然后你可以获取这个文件列表,并创build一个包含你的类的数据结构。

该项目ldapbeans提供了一个类扫描仪 ,这样做。

使用依赖项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)); } }); 

这是另一个选项,轻微修改上面/下面的另一个答案:

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

布伦特 – 关联是一种方式的原因与CLASSPATH的任何组件中的任何类都可以在任何包(java / javax除外)中声明自己的事实有关。 因此,在给定的“包”中没有映射所有的类,因为没有人知道也不能知道。 你可以明天更新一个jar文件并删除或添加类。 这就好比想要在全世界所有国家都列出所有名叫约翰/乔恩/约翰的人 – 我们都不是无所不知的,所以我们谁也不会有正确的答案。