3

我遇到了尝试从类本身的 .class 文件所在的同一目录中读取一些配置文件的代码:

File[] configFiles = new File(
    this.getClass().getResource(".").getPath()).listFiles(new FilenameFilter() {
        public boolean accept(File dir, String name) {
            return name.endsWith(".xml");
        }
});

显然这在某些情况下有效(也许在 Resin 中运行代码时),但对我来说,运行 Tomcat 时,它会因 NPE 而失败,因为getClass().getResource(".")返回null.

一位同事建议创建另一个配置文件,其中包含所有“.xml”配置文件的列表(它确实可以在这里工作,因为它保持完全静态),并且您不应该真的尝试在 Java 中做这样的事情。

不过,我想知道是否有一些普遍适用的好方法来获取给定 .class 文件所在目录的路径?我想你可以从 .class 文件本身的路径中获取它,如下所示:

new File(this.getClass().getResource("MyClass.class").getPath()).getParent()

...但这是唯一/最干净的方式吗?

编辑:为了澄清,假设我们知道这用于以这样的方式部署的应用程序中 MyClass.class 将始终从磁盘上的 .class 文件中读取,并且资源将在同一目录中。

4

5 回答 5

3

我知道这个帖子很旧,但它是谷歌搜索的最高结果,对我来说,这里没有令人满意的答案。这是我写的一些代码,对我很有用。当然,需要注意的是它可能没有从磁盘加载,但它解释了这一点,并在这种情况下返回 null。这适用于查找“容器”,即类的根位置,无论是 jar 还是文件夹。这可能不直接满足您的需求。如果没有,请随意删除您确实需要的代码部分。

/**
 * Returns the container url for this class. This varies based on whether or
 * not the class files are in a zip/jar or not, so this method standardizes
 * that. The method may return null, if the class is a dynamically generated
 * class (perhaps with asm, or a proxy class)
 *
 * @param c The class to find the container for
 * @return
 */
public static String GetClassContainer(Class c) {
    if (c == null) {
        throw new NullPointerException("The Class passed to this method may not be null");
    }
    try {
        while(c.isMemberClass() || c.isAnonymousClass()){
            c = c.getEnclosingClass(); //Get the actual enclosing file
        }
        if (c.getProtectionDomain().getCodeSource() == null) {
            //This is a proxy or other dynamically generated class, and has no physical container,
            //so just return null.
            return null;
        }
        String packageRoot;
        try {
            //This is the full path to THIS file, but we need to get the package root.
            String thisClass = c.getResource(c.getSimpleName() + ".class").toString();
            packageRoot = StringUtils.replaceLast(thisClass, Pattern.quote(c.getName().replaceAll("\\.", "/") + ".class"), "");
            if(packageRoot.endsWith("!/")){
                packageRoot = StringUtils.replaceLast(packageRoot, "!/", "");
            }
        } catch (Exception e) {
            //Hmm, ok, try this then
            packageRoot = c.getProtectionDomain().getCodeSource().getLocation().toString();
        }
        packageRoot = URLDecoder.decode(packageRoot, "UTF-8");
        return packageRoot;
    } catch (Exception e) {
        throw new RuntimeException("While interrogating " + c.getName() + ", an unexpected exception was thrown.", e);
    }
}
于 2013-07-24T15:08:58.223 回答
2

如果资源与 .class 文件位于同一文件夹中,则它应该可以通过类路径访问,并且可以直接通过 getResourceAsStream 加载。

this.getClass().getResourceAsStream( "filename.xml" )

如前所述,类本身可以远程加载,也可以在没有正确“路径”的地方加载(例如,从 jarfile 加载)

于 2009-07-27T14:21:58.887 回答
2

是什么让您认为磁盘上自己的目录中有一个类文件?

一个类可以是:

  • 完全在内存中创建
  • 从网络连接加载
  • 从 jar 文件加载

您可以获取用于创建类本身的 URL,如果以它开头,file://那么您可以获得其余的……但它不适用于所有类。

于 2009-07-27T14:19:00.530 回答
1

我同意您的同事的观点,即 Java 类加载并非旨在处理此用例。Sun Facelets 使用类似的策略,假设 URL 可以映射到文件,但它并不漂亮。我同意 Jon 的评论,即根据您的部署假设,您的 getResource 解决方案可能是最干净的。既然你问这是否是唯一的方法,我也会提供 getClass().getProtectionDomain().getCodeSource().getLocation(),它应该是类加载器实际加载你的类的 URL(你需要附加您的类包的子目录)。此策略也具有相同的 URL-to-File 假设,因此在这方面也没有更好的选择。我想不出其他通用解决方案。

请注意,getResource 返回一个编码的 URL,这意味着您不应该直接使用 getPath()。特别是,空间会导致问题,但如果您可以控制环境,这可能不是问题。考虑使用新文件(URL.toURI())。

于 2009-07-28T04:43:14.523 回答
0

我想,您可能对Spring Framework中的PathMatchingResourcePatternResolver感兴趣。您可以直接在代码中使用它来导航配置文件,也可以在此处查找实现。

于 2009-07-28T05:04:24.793 回答