提取扩展“Randomizer”的类来自类路径中的所有 jarfile

发布于 2024-11-24 08:49:13 字数 6811 浏览 1 评论 0原文

这个主题确实说出了我的目标,但我对实施感到困惑。我有一个程序,它接受扩展我的 Randomizer 类的不同对象。我想这样做,以便人们可以在类路径中放置一个 JAR 文件,程序将在运行时搜索它,并将其添加到主程序中。到目前为止,这是我尝试过的,但当我意识到 java.util.jar.JarFile 无法为您提供 Classes 或 Method 时,我停止了

因为它依赖于它,所以我不妨提到我的类 ArrayPP 类似于 ArrayList,但具有更多方法。此处所示的 addAll 方法的功能类似于其 add 方法,但具有多个参数或泛型类型 T 的对象数组。

  private static Randomizer[] loadExternalRandomizers() throws IOException
  {
    java.io.File classPath = new java.io.File(System.getProperty("user.dir"));
    ArrayPP<Randomizer> r = new ArrayPP<>();
    if (classPath.isDirectory())
    {
      r.addAll(getRandomizersIn(classPath));
    }
    return r.toArray();
  }

  private static Randomizer[] getRandomizersIn(File dir) throws IOException
  {
    ArrayPP<Randomizer> r = new ArrayPP<>();
    java.io.File fs[] = dir.listFiles(new java.io.FileFilter() {

      @Override
      public boolean accept(File pathname)
      {
        return pathname.isDirectory() || pathname.toString().endsWith(".jar");
      }
    });
    java.util.jar.JarFile jr;
    java.util.Enumeration<java.util.jar.JarEntry> entries;
    java.util.jar.JarEntry thisEntry;
    for (java.io.File f : fs)
    {
      if (f.isDirectory())
      {
        r.addAll(getRandomizersIn(f));
        continue;
      }
      jr = new java.util.jar.JarFile(f);
      entries = jr.entries();
      while (entries.hasMoreElements())
      {
        thisEntry = entries.nextElement();
        //if (the jar file contains a class that extends Randomizer
        //  add that class to r
      }
    }
    return r.toArray();
  }

我正在 Java 7 上构建它,如果有帮助的话。我也希望在不使用任何库的情况下做到这一点。

实现解决方案


我尝试实现 Ryan Stewart 描述的解决方案,如下所示。我正在使用名为 BHR2 - Ranger.jar 的测试 JAR,其中包含一个扩展 Randomizer 的类,称为 Ranger,位于包bhr2.plugins中。 JAR 在其 META-INF\services 文件夹中包含一个名为 bhr2.plugins.Ranger 的文件,其中一行内容为 < code>bhr2.Randomizer # 抽象随机化器。

  private static ArrayPP<Randomizer> loadExternalRandomizers() throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
  {
    java.io.File classPath = new java.io.File(System.getProperty("user.dir"));
    ArrayPP<Randomizer> r = new ArrayPP<>();
    if (classPath.isDirectory())
    {
      r.addAll(getRandomizersIn(classPath));
    }
    return r;
  }

  private static int depth = 0;
  private static ArrayPP<Randomizer> getRandomizersIn(File dir) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
  {
    ArrayPP<Randomizer> r = new ArrayPP<>();
    java.io.File fs[] = dir.listFiles(new java.io.FileFilter() {

      @Override
      public boolean accept(File pathname)
      {
        return pathname.isDirectory() || pathname.toString().endsWith(".jar");
      }
    });
    for (java.io.File f : fs)
    {
      for (int i=0; i < depth; i++)
        System.out.print("  ");
      System.out.println(f);
      if (f.isDirectory())
      {
        if (depth < 4)
        {
          depth++;
          r.addAll(getRandomizersIn(f));
          depth--;
        }
        else
          System.out.println("Skipping directory due to depth");
        continue;
      }
        java.util.ServiceLoader<Randomizer> sl = ServiceLoader.loadInstalled(Randomizer.class);

        for (Randomizer rand : sl)
        {
          r.add(rand);
          System.out.println("adding " + rand);
        }
    }
    return r == null || r.isEmpty() ? new ArrayPP<Randomizer>() : r;
  }

但是当我运行它时,这就是在它开始执行其他操作之前我得到的所有输出:

I:\Java\NetBeansProjects\BHRandomizer2\nbproject
  I:\Java\NetBeansProjects\BHRandomizer2\nbproject\private
I:\Java\NetBeansProjects\BHRandomizer2\src
  I:\Java\NetBeansProjects\BHRandomizer2\src\bhr2
    I:\Java\NetBeansProjects\BHRandomizer2\src\bhr2\resources
    I:\Java\NetBeansProjects\BHRandomizer2\src\bhr2\randomizers
  I:\Java\NetBeansProjects\BHRandomizer2\src\bht
    I:\Java\NetBeansProjects\BHRandomizer2\src\bht\test
    I:\Java\NetBeansProjects\BHRandomizer2\src\bht\tools
      I:\Java\NetBeansProjects\BHRandomizer2\src\bht\tools\comps
        I:\Java\NetBeansProjects\BHRandomizer2\src\bht\tools\comps\gameboard
Skipping directory due to depth
      I:\Java\NetBeansProjects\BHRandomizer2\src\bht\tools\effects
      I:\Java\NetBeansProjects\BHRandomizer2\src\bht\tools\misc
      I:\Java\NetBeansProjects\BHRandomizer2\src\bht\tools\utilities
    I:\Java\NetBeansProjects\BHRandomizer2\src\bht\resources
I:\Java\NetBeansProjects\BHRandomizer2\lib
  I:\Java\NetBeansProjects\BHRandomizer2\lib\CopyLibs
    I:\Java\NetBeansProjects\BHRandomizer2\lib\CopyLibs\org-netbeans-modules-java-j2seproject-copylibstask.jar
  I:\Java\NetBeansProjects\BHRandomizer2\lib\swing-layout
    I:\Java\NetBeansProjects\BHRandomizer2\lib\swing-layout\swing-layout-1.0.4.jar
I:\Java\NetBeansProjects\BHRandomizer2\BHR2 - Ranger
  I:\Java\NetBeansProjects\BHRandomizer2\BHR2 - Ranger\META-INF
  I:\Java\NetBeansProjects\BHRandomizer2\BHR2 - Ranger\bhr2
    I:\Java\NetBeansProjects\BHRandomizer2\BHR2 - Ranger\bhr2\plugins
I:\Java\NetBeansProjects\BHRandomizer2\build
  I:\Java\NetBeansProjects\BHRandomizer2\build\classes
    I:\Java\NetBeansProjects\BHRandomizer2\build\classes\bhr2
      I:\Java\NetBeansProjects\BHRandomizer2\build\classes\bhr2\randomizers
      I:\Java\NetBeansProjects\BHRandomizer2\build\classes\bhr2\resources
    I:\Java\NetBeansProjects\BHRandomizer2\build\classes\bht
      I:\Java\NetBeansProjects\BHRandomizer2\build\classes\bht\tools
        I:\Java\NetBeansProjects\BHRandomizer2\build\classes\bht\tools\comps
Skipping directory due to depth
        I:\Java\NetBeansProjects\BHRandomizer2\build\classes\bht\tools\utilities
Skipping directory due to depth
        I:\Java\NetBeansProjects\BHRandomizer2\build\classes\bht\tools\effects
Skipping directory due to depth
        I:\Java\NetBeansProjects\BHRandomizer2\build\classes\bht\tools\misc
Skipping directory due to depth
      I:\Java\NetBeansProjects\BHRandomizer2\build\classes\bht\resources
      I:\Java\NetBeansProjects\BHRandomizer2\build\classes\bht\test
    I:\Java\NetBeansProjects\BHRandomizer2\build\classes\META-INF
  I:\Java\NetBeansProjects\BHRandomizer2\build\empty
  I:\Java\NetBeansProjects\BHRandomizer2\build\generated-sources
    I:\Java\NetBeansProjects\BHRandomizer2\build\generated-sources\ap-source-output
I:\Java\NetBeansProjects\BHRandomizer2\dist
  I:\Java\NetBeansProjects\BHRandomizer2\dist\BHRandomizer2.jar
I:\Java\NetBeansProjects\BHRandomizer2\BHR2 - Ranger.jar

The subject really says my goal, but I'm stumped as to the implementation. I have a program that takes in different objects that extend my Randomizer class. I want to make it so that one can place a JAR file in the classpath and the program will search for it upon running it, and add it to the main program. This is what I tried, so far, but I stopped when I realized that a java.util.jar.JarFile can't give you Classes or Methods.

since it relies on it, I might as well mention that my class ArrayPP<T> is like ArrayList, but with alot more methods. Its addAll method, shown here, functions like its add method, but with multiple arguments or an array of objects of generic type T.

  private static Randomizer[] loadExternalRandomizers() throws IOException
  {
    java.io.File classPath = new java.io.File(System.getProperty("user.dir"));
    ArrayPP<Randomizer> r = new ArrayPP<>();
    if (classPath.isDirectory())
    {
      r.addAll(getRandomizersIn(classPath));
    }
    return r.toArray();
  }

  private static Randomizer[] getRandomizersIn(File dir) throws IOException
  {
    ArrayPP<Randomizer> r = new ArrayPP<>();
    java.io.File fs[] = dir.listFiles(new java.io.FileFilter() {

      @Override
      public boolean accept(File pathname)
      {
        return pathname.isDirectory() || pathname.toString().endsWith(".jar");
      }
    });
    java.util.jar.JarFile jr;
    java.util.Enumeration<java.util.jar.JarEntry> entries;
    java.util.jar.JarEntry thisEntry;
    for (java.io.File f : fs)
    {
      if (f.isDirectory())
      {
        r.addAll(getRandomizersIn(f));
        continue;
      }
      jr = new java.util.jar.JarFile(f);
      entries = jr.entries();
      while (entries.hasMoreElements())
      {
        thisEntry = entries.nextElement();
        //if (the jar file contains a class that extends Randomizer
        //  add that class to r
      }
    }
    return r.toArray();
  }

I'm building it on Java 7, if that helps. I'm also looking to do this without using any libraries.

Implementing a solution


I've tried implementing the solution described by Ryan Stewart, and it is shown below. I'm working it with a test JAR called BHR2 - Ranger.jar, which contains one class that extends Randomizer, called Ranger, in the package bhr2.plugins. The JAR contains, within its META-INF\services folder, one file called bhr2.plugins.Ranger with one line in it which reads bhr2.Randomizer # Abstract Randomizer.

  private static ArrayPP<Randomizer> loadExternalRandomizers() throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
  {
    java.io.File classPath = new java.io.File(System.getProperty("user.dir"));
    ArrayPP<Randomizer> r = new ArrayPP<>();
    if (classPath.isDirectory())
    {
      r.addAll(getRandomizersIn(classPath));
    }
    return r;
  }

  private static int depth = 0;
  private static ArrayPP<Randomizer> getRandomizersIn(File dir) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
  {
    ArrayPP<Randomizer> r = new ArrayPP<>();
    java.io.File fs[] = dir.listFiles(new java.io.FileFilter() {

      @Override
      public boolean accept(File pathname)
      {
        return pathname.isDirectory() || pathname.toString().endsWith(".jar");
      }
    });
    for (java.io.File f : fs)
    {
      for (int i=0; i < depth; i++)
        System.out.print("  ");
      System.out.println(f);
      if (f.isDirectory())
      {
        if (depth < 4)
        {
          depth++;
          r.addAll(getRandomizersIn(f));
          depth--;
        }
        else
          System.out.println("Skipping directory due to depth");
        continue;
      }
        java.util.ServiceLoader<Randomizer> sl = ServiceLoader.loadInstalled(Randomizer.class);

        for (Randomizer rand : sl)
        {
          r.add(rand);
          System.out.println("adding " + rand);
        }
    }
    return r == null || r.isEmpty() ? new ArrayPP<Randomizer>() : r;
  }

But when I run it, this is all I get as an output before it starts doing other things:

I:\Java\NetBeansProjects\BHRandomizer2\nbproject
  I:\Java\NetBeansProjects\BHRandomizer2\nbproject\private
I:\Java\NetBeansProjects\BHRandomizer2\src
  I:\Java\NetBeansProjects\BHRandomizer2\src\bhr2
    I:\Java\NetBeansProjects\BHRandomizer2\src\bhr2\resources
    I:\Java\NetBeansProjects\BHRandomizer2\src\bhr2\randomizers
  I:\Java\NetBeansProjects\BHRandomizer2\src\bht
    I:\Java\NetBeansProjects\BHRandomizer2\src\bht\test
    I:\Java\NetBeansProjects\BHRandomizer2\src\bht\tools
      I:\Java\NetBeansProjects\BHRandomizer2\src\bht\tools\comps
        I:\Java\NetBeansProjects\BHRandomizer2\src\bht\tools\comps\gameboard
Skipping directory due to depth
      I:\Java\NetBeansProjects\BHRandomizer2\src\bht\tools\effects
      I:\Java\NetBeansProjects\BHRandomizer2\src\bht\tools\misc
      I:\Java\NetBeansProjects\BHRandomizer2\src\bht\tools\utilities
    I:\Java\NetBeansProjects\BHRandomizer2\src\bht\resources
I:\Java\NetBeansProjects\BHRandomizer2\lib
  I:\Java\NetBeansProjects\BHRandomizer2\lib\CopyLibs
    I:\Java\NetBeansProjects\BHRandomizer2\lib\CopyLibs\org-netbeans-modules-java-j2seproject-copylibstask.jar
  I:\Java\NetBeansProjects\BHRandomizer2\lib\swing-layout
    I:\Java\NetBeansProjects\BHRandomizer2\lib\swing-layout\swing-layout-1.0.4.jar
I:\Java\NetBeansProjects\BHRandomizer2\BHR2 - Ranger
  I:\Java\NetBeansProjects\BHRandomizer2\BHR2 - Ranger\META-INF
  I:\Java\NetBeansProjects\BHRandomizer2\BHR2 - Ranger\bhr2
    I:\Java\NetBeansProjects\BHRandomizer2\BHR2 - Ranger\bhr2\plugins
I:\Java\NetBeansProjects\BHRandomizer2\build
  I:\Java\NetBeansProjects\BHRandomizer2\build\classes
    I:\Java\NetBeansProjects\BHRandomizer2\build\classes\bhr2
      I:\Java\NetBeansProjects\BHRandomizer2\build\classes\bhr2\randomizers
      I:\Java\NetBeansProjects\BHRandomizer2\build\classes\bhr2\resources
    I:\Java\NetBeansProjects\BHRandomizer2\build\classes\bht
      I:\Java\NetBeansProjects\BHRandomizer2\build\classes\bht\tools
        I:\Java\NetBeansProjects\BHRandomizer2\build\classes\bht\tools\comps
Skipping directory due to depth
        I:\Java\NetBeansProjects\BHRandomizer2\build\classes\bht\tools\utilities
Skipping directory due to depth
        I:\Java\NetBeansProjects\BHRandomizer2\build\classes\bht\tools\effects
Skipping directory due to depth
        I:\Java\NetBeansProjects\BHRandomizer2\build\classes\bht\tools\misc
Skipping directory due to depth
      I:\Java\NetBeansProjects\BHRandomizer2\build\classes\bht\resources
      I:\Java\NetBeansProjects\BHRandomizer2\build\classes\bht\test
    I:\Java\NetBeansProjects\BHRandomizer2\build\classes\META-INF
  I:\Java\NetBeansProjects\BHRandomizer2\build\empty
  I:\Java\NetBeansProjects\BHRandomizer2\build\generated-sources
    I:\Java\NetBeansProjects\BHRandomizer2\build\generated-sources\ap-source-output
I:\Java\NetBeansProjects\BHRandomizer2\dist
  I:\Java\NetBeansProjects\BHRandomizer2\dist\BHRandomizer2.jar
I:\Java\NetBeansProjects\BHRandomizer2\BHR2 - Ranger.jar

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

是伱的 2024-12-01 08:49:13

此问题的典型解决方案是构建在 META-INF 中包含一个文件的 jar 文件,该文件告诉您的程序要从该 jar 加载哪些类。 Spring 自定义命名空间处理程序JDBC驱动程序,例如,已加载 这样。

不要弄清楚如何实际扫描 jar 中的所有类(没有人这样做,所以我预计这是不可能/不可行的),而是让您的代码在类路径上的每个 jar 中查找特定文件,其中列出了它包含的 Randomizer 实现。例如,期望名为 META-INF/randomizers.list 的文件具有一个类名列表,每行一个,它们是该 jar 中实现随机化器的类。读取文件,对于每一行,使用 Class.forName() 按名称加载类,然后newInstance() 实例化它。

编辑:用于从类路径中的任何位置加载“列表”文件:

Enumeration<URL> resources = getClassLoader().getResources(
    "/META-INF/randomizers.list");
while (resources.hasMoreElements()) {
    URL url = resources.nextElement();
    // Load the Randomizer(s) specified in this file
}

编辑:所以事实证明 JDK 公开了它用于此类事情的机制,我对此以前不知道。只需使用 ServiceLoader 即可。 文档解释了如何使用它,以及我也编写了一个如何使用它的示例。您可以在 github 上找到代码或者直接克隆并自己运行它:

git clone git://github.com/zzantozz/testbed.git tmp
cd tmp
mvn install -pl serviceloader-example/service-usage -am
mvn -q exec:java -D exec.mainClass=rds.serviceloader.ServiceLoaderExample -pl serviceloader-example/service-usage

该示例由五个模块组成:一个定义服务接口,三个定义单独的服务实现,一个使用 ServiceLoader 加载实现。最后一个称为“服务使用”,演示如何使用 ServiceLoader 类加载其他三个模块/jar 中定义的三个服务,这些模块/jar 实现第一个模块/jar 中定义的接口。

编辑:由于您似乎在使用示例项目时遇到了问题,因此以下是基础知识。

  1. 每个包含一个或多个 Randomizer 实现的 jar 文件都应包含一个名为 META-INF/services/com.foo.Randomizer 的文件(其中 com.foo 是您的包名称)。
  2. 该文件应包含一个类名列表,每行一个,每个类名都是 Randomizer 的一个实现。
  3. 这些文件就位后,您所需要做的就是获取所有 Randomizer 实例

    ServiceLoader;加载器 = ServiceLoader.load(Randomizer.class);
    for (随机化器随机化器:加载器) {
        randomizer.doWhatever();
    }
    

A typical solution to this problem is to build jar files that have a file in META-INF that tells your program what class(es) to load from that jar. Spring custom namespace handlers and JDBC drivers, for example, are loaded in this way.

Instead of figuring out how to actually scan all the classes in a jar, which nobody does, so I expect isn't possible/feasible, make your code look for a specific file in each jar on the classpath which lists the Randomizer implementations it contains. For instance, expect a file named META-INF/randomizers.list to have a list of class names, one per line, which are classes in that jar that implement your Randomizer. Read the file, and for each line, use Class.forName() to load the class by name, then newInstance() to instantiate it.

Edit: For loading your "list" file from everywhere in the classpath:

Enumeration<URL> resources = getClassLoader().getResources(
    "/META-INF/randomizers.list");
while (resources.hasMoreElements()) {
    URL url = resources.nextElement();
    // Load the Randomizer(s) specified in this file
}

Edit: So it turns out the JDK exposes the mechanism it uses for this kind of thing, which I didn't know about before. Just use a ServiceLoader. The docs explain how to use it, and I've coded up an example of how to use it, too. You can find the code on github or simply clone and run it yourself:

git clone git://github.com/zzantozz/testbed.git tmp
cd tmp
mvn install -pl serviceloader-example/service-usage -am
mvn -q exec:java -D exec.mainClass=rds.serviceloader.ServiceLoaderExample -pl serviceloader-example/service-usage

The example consists of five modules: one that defines a service interface, three that define separate service implementations, and one that loads the implementations using ServiceLoader. That last is called "service-usage" and demonstrates how to use the ServiceLoader class to load the three services defined in the other three modules/jars that implement the interface defined in the first module/jar.

Edit: Since you seem to be having trouble with the sample project, here are the basics.

  1. Each jar file that contains one or more Randomizer implementations should contain a file named META-INF/services/com.foo.Randomizer (where com.foo is your package name).
  2. This file should contain a list of class names, one per line, each of which is an implementation of Randomizer.
  3. With those files in place, all you have to do to get all the Randomizer instances is

    ServiceLoader<Randomizer> loader = ServiceLoader.load(Randomizer.class);
    for (Randomizer randomizer : loader) {
        randomizer.doWhatever();
    }
    
開玄 2024-12-01 08:49:13

从清单中获取类列表当然是更好的解决方案。但如果您不希望罐子包含此类信息,您可以使用以下方法:

  private static void scanClasses(File file) throws MalformedURLException, IOException {
    ClassLoader classLoader = new URLClassLoader(new URL[]{ file.toURI().toURL() });
    JarFile jar = new JarFile(file.getAbsoluteFile());
    Enumeration<JarEntry> jarEntries = jar.entries();
    while (jarEntries.hasMoreElements()) {
      JarEntry je = jarEntries.nextElement();
      if (je.getName().endsWith(".class")) {
        String clazzName = je.getName();
        clazzName = clazzName.substring(0, clazzName.length() - ".class".length()).replaceAll("/", ".");
        clazzName = clazzName.replaceAll("/", ".");
        try {
          Class clazz = Class.forName(clazzName, false, classLoader);
          if (Randomizer.class.isAssignableFrom(clazz)) {
            System.out.println("Found Randomizer: " + clazz);
          }
        } catch (ClassNotFoundException ex) {
          // this really should not happen, 
          // since we *know* the class exists
          throw new AssertionError(ex.getMessage());
        }
      }
    }
  }

Fetching the class list from the Manifest is of course the preferable solution. But if you don't expect the jars to contain such information, you can fall back to this:

  private static void scanClasses(File file) throws MalformedURLException, IOException {
    ClassLoader classLoader = new URLClassLoader(new URL[]{ file.toURI().toURL() });
    JarFile jar = new JarFile(file.getAbsoluteFile());
    Enumeration<JarEntry> jarEntries = jar.entries();
    while (jarEntries.hasMoreElements()) {
      JarEntry je = jarEntries.nextElement();
      if (je.getName().endsWith(".class")) {
        String clazzName = je.getName();
        clazzName = clazzName.substring(0, clazzName.length() - ".class".length()).replaceAll("/", ".");
        clazzName = clazzName.replaceAll("/", ".");
        try {
          Class clazz = Class.forName(clazzName, false, classLoader);
          if (Randomizer.class.isAssignableFrom(clazz)) {
            System.out.println("Found Randomizer: " + clazz);
          }
        } catch (ClassNotFoundException ex) {
          // this really should not happen, 
          // since we *know* the class exists
          throw new AssertionError(ex.getMessage());
        }
      }
    }
  }
恋竹姑娘 2024-12-01 08:49:13

这是我用来检索属于包和任何子包的所有类的代码。您只需在返回的类列表中搜索那些使用反射实现 Randomizer 的类即可。

/**
 * Scans all classes accessible from the context class loader which belong
 * to the given package and subpackages.
 * <p>
 * Inspired from post on: {@code http://snippets.dzone.com/posts/show/4831}
 */
public static List<Class<?>> getClasses(String packageName)
        throws ClassNotFoundException, IOException {

    // Retrieving current class loader
    final ClassLoader classLoader
            = Thread.currentThread().getContextClassLoader();

    // Computing path from the package name
    final String path = packageName.replace('.', '/');

    // Retrieving all accessible resources
    final Enumeration<URL> resources = classLoader.getResources(path);
    final List<File> dirs = new ArrayList<File>();

    while (resources.hasMoreElements()) {
        final URL resource = resources.nextElement();
        final String fileName = resource.getFile();
        final String fileNameDecoded = URLDecoder.decode(fileName, "UTF-8");
        dirs.add(new File(fileNameDecoded));
    }

    // Preparing result
    final ArrayList<Class<?>> classes = new ArrayList<Class<?>>();

    // Processing each resource recursively
    for (File directory : dirs) {
        classes.addAll(findClasses(directory, packageName));
    }

    // Returning result
    return classes;

}

/**
 * Recursive method used to find all classes in a given directory and
 * subdirs.
 */
public static List<Class<?>> findClasses(File directory, String packageName)
        throws ClassNotFoundException {

    // Preparing result
    final List<Class<?>> classes = new ArrayList<Class<?>>();

    if ( directory == null )
        return classes;

    if (!directory.exists())
        return classes;

    // Retrieving the files in the directory
    final File[] files = directory.listFiles();

    for (File file : files) {

        final String fileName = file.getName();

        // Do we need to go recursive?
        if (file.isDirectory()) {

            classes.addAll(findClasses(file, packageName + "." + fileName));

        } else if (fileName.endsWith(".class") && !fileName.contains("$")) {

            Class<?> _class;

                _class = Class.forName(packageName + '.'
                        + fileName.substring(0, fileName.length() - 6));

            classes.add(_class);

        }

    }

    return classes;

}

Here here is the code I am using to retrieve all the classes belonging to a package and any sub-packages. You just need to search the list of returned classes for those implementing Randomizer using reflection.

/**
 * Scans all classes accessible from the context class loader which belong
 * to the given package and subpackages.
 * <p>
 * Inspired from post on: {@code http://snippets.dzone.com/posts/show/4831}
 */
public static List<Class<?>> getClasses(String packageName)
        throws ClassNotFoundException, IOException {

    // Retrieving current class loader
    final ClassLoader classLoader
            = Thread.currentThread().getContextClassLoader();

    // Computing path from the package name
    final String path = packageName.replace('.', '/');

    // Retrieving all accessible resources
    final Enumeration<URL> resources = classLoader.getResources(path);
    final List<File> dirs = new ArrayList<File>();

    while (resources.hasMoreElements()) {
        final URL resource = resources.nextElement();
        final String fileName = resource.getFile();
        final String fileNameDecoded = URLDecoder.decode(fileName, "UTF-8");
        dirs.add(new File(fileNameDecoded));
    }

    // Preparing result
    final ArrayList<Class<?>> classes = new ArrayList<Class<?>>();

    // Processing each resource recursively
    for (File directory : dirs) {
        classes.addAll(findClasses(directory, packageName));
    }

    // Returning result
    return classes;

}

/**
 * Recursive method used to find all classes in a given directory and
 * subdirs.
 */
public static List<Class<?>> findClasses(File directory, String packageName)
        throws ClassNotFoundException {

    // Preparing result
    final List<Class<?>> classes = new ArrayList<Class<?>>();

    if ( directory == null )
        return classes;

    if (!directory.exists())
        return classes;

    // Retrieving the files in the directory
    final File[] files = directory.listFiles();

    for (File file : files) {

        final String fileName = file.getName();

        // Do we need to go recursive?
        if (file.isDirectory()) {

            classes.addAll(findClasses(file, packageName + "." + fileName));

        } else if (fileName.endsWith(".class") && !fileName.contains("$")) {

            Class<?> _class;

                _class = Class.forName(packageName + '.'
                        + fileName.substring(0, fileName.length() - 6));

            classes.add(_class);

        }

    }

    return classes;

}
偏爱自由 2024-12-01 08:49:13

我认为你应该坚持使用 java ServiceLoader。那么你就不需要自己摆弄罐子了。
这是一个很好的教程:
http://java.sun.com/developer/technicalArticles/javase/extensible /index.html

如果您开发可扩展的桌面应用程序,netbeans 平台可能是适合您的解决方案。它的学习曲线较高,但非常值得。
http://netbeans.org/features/platform/index.html

I think you should stick with the java ServiceLoader. Then you don't need to fiddle with jars yourself.
Here's a good tutorial:
http://java.sun.com/developer/technicalArticles/javase/extensible/index.html

If your developing an extensible desktop application the netbeans platform might be the right solution for you. It has a higher learning curve but it is quite worth it.
http://netbeans.org/features/platform/index.html

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文