Java包内省

Java包内省,java,class,packages,introspection,Java,Class,Packages,Introspection,如何在一个包中获取所有类?您不能。类可以通过许多不同的类装入器进入,包括远程类装入器。没有全局方法。也就是说,如果您知道类来自何处,您可以浏览jar文件或文件系统的目录。假设可以在本地找到类,则其中有一个片段正好满足您的需要: private static Class[] getClasses(String packageName) throws ClassNotFoundException, IOException { ClassLoader classLoader = Thread.

如何在一个包中获取所有类?

您不能。类可以通过许多不同的类装入器进入,包括远程类装入器。

没有全局方法。也就是说,如果您知道类来自何处,您可以浏览jar文件或文件系统的目录。

假设可以在本地找到类,则其中有一个片段正好满足您的需要:

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

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;
}
private静态类[]getClasses(字符串packageName)
抛出ClassNotFoundException,IOException{
ClassLoader ClassLoader=Thread.currentThread().getContextClassLoader();
断言类加载器!=null;
字符串路径=packageName.replace('.','/');
枚举资源=classLoader.getResources(路径);
List dirs=new ArrayList();
while(resources.hasMoreElements()){
URL resource=resources.nextElement();
添加(新文件(resource.getFile());
}
ArrayList类=新的ArrayList();
for(文件目录:dirs){
addAll(findClasses(目录,packageName));
}
返回classes.toArray(新类[classes.size()]);
}
私有静态列表FindClass(文件目录、字符串packageName)引发ClassNotFoundException{
列表类=新的ArrayList();
如果(!directory.exists()){
返回类;
}
File[]files=目录.listFiles();
用于(文件:文件){
if(file.isDirectory()){
assert!file.getName()包含(“.”);
addAll(findClasses(文件,packageName+““+file.getName());
}else if(file.getName().endsWith(“.class”)){
Class.add(Class.forName(packageName+'.+file.getName().substring(0,file.getName().length()-6));
}
}
返回类;
}

Java没有发现


大多数能够添加(发现)新类的产品要么有一个描述“程序扩展名”的文本文件,要么有一个特定的目录,您可以在其中放置类或jar,这些类或jar使用@JG descripted这样的技巧。(这就是eclipse所做的,对于用户可以手动添加新模块的任何解决方案都建议使用eclipse)

根据JG发布的想法,这里有一个更完整的方法来解决JAR的问题

/**
 * Scans all classloaders for the current thread for loaded jars, and then scans
 * each jar for the package name in question, listing all classes directly under
 * the package name in question. Assumes directory structure in jar file and class
 * package naming follow java conventions (i.e. com.example.test.MyTest would be in
 * /com/example/test/MyTest.class)
 */
public Collection<Class> getClassesForPackage(String packageName) throws Exception {
  String packagePath = packageName.replace(".", "/");
  ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
  Set<URL> jarUrls = new HashSet<URL>();

  while (classLoader != null) {
    if (classLoader instanceof URLClassLoader)
      for (URL url : ((URLClassLoader) classLoader).getURLs())
        if (url.getFile().endsWith(".jar")  // may want better way to detect jar files
          jarUrls.add(url);

    classLoader = classLoader.getParent();
  }

  Set<Class> classes = new HashSet<Class>();

  for (URL url : jarUrls) {
    JarInputStream stream = new JarInputStream(url.openStream()); // may want better way to open url connections
    JarEntry entry = stream.getNextJarEntry();

    while (entry != null) {
      String name = entry.getName();
      int i = name.lastIndexOf("/");

      if (i > 0 && name.endsWith(".class") && name.substring(0, i).equals(packagePath)) 
        classes.add(Class.forName(name.substring(0, name.length() - 6).replace("/", ".")));

      entry = stream.getNextJarEntry();
    }

    stream.close();
  }

  return classes;
}
/**
*扫描当前线程的所有类加载器以查找已加载的jar,然后扫描
*每个jar都包含有问题的包名,并直接列出了下面的所有类
*有问题的包名称。采用jar文件和类中的目录结构
*包命名遵循java约定(例如,com.example.test.MyTest将在
*/com/example/test/MyTest.class)
*/
公共集合getClassesForPackage(字符串packageName)引发异常{
字符串packagePath=packageName.replace(“.”,“/”);
ClassLoader ClassLoader=Thread.currentThread().getContextClassLoader();
Set jarurl=newhashset();
while(classLoader!=null){
if(类加载器实例URLClassLoader)
对于(URL:((URLClassLoader)classLoader.getURL())
if(url.getFile().endsWith(“.jar”)//可能需要更好的方法来检测jar文件
jarurl.add(url);
classLoader=classLoader.getParent();
}
Set classes=新的HashSet();
for(URL:jarURL){
JarInputStream=新的JarInputStream(url.openStream());//可能需要更好的方法来打开url连接
JarEntry=stream.getNextJarEntry();
while(条目!=null){
String name=entry.getName();
int i=name.lastIndexOf(“/”);
if(i>0&&name.endsWith(“.class”)&&name.substring(0,i).equals(packagePath))
add(Class.forName(name.substring(0,name.length()-6).replace(“/”,”));
entry=stream.getNextJarEntry();
}
stream.close();
}
返回类;
}

以上的答案概括了所需的功能。但是,可以找到关于该主题的更多详细信息,并且。

这是否适用于jar类?据我所知,如果您有一个jar(-1),这将失败,但对于查找.class文件(+1)来说是一个不错的例行程序,所以您收支平衡:)上面的代码不仅假设来自给定包的类是本地的,而且还假设它们都是由同一个类加载器加载的,并且它们还没有被打包。对于像Eclipse这样的系统来说,这仍然是一个有用的开始,在这个系统中,jar被放入目录中,并在下一次引导时被发现。一个合理的解决方案,但非常有限(而且因为它目前不支持JAR,没有那么有用),不要忘记插件的SPIAPI。这些为插件框架提供了一个相当优雅的解决方案。-1没有文档,甚至没有关于其功能的提示,+2因为它看起来像是扫描类路径并包含JAR!所以你得到了一个+1:)(好吧,这里有一个关于它的作用的提示。我收回它)@吐露居好极了!只需将
.equals(packagePath)
更改为
.startsWith(packagePath)
,以满足我的需要。谢谢。另请注意。我的库FastClasspathScanner处理绝大多数常见的类加载器和类路径规范机制。