获取给定类文件的目录路径

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文件中读取,并且资源也将在该目录中。


发现了这个相关的问题:https://dev59.com/jHRA5IYBdhLWcg3w8SeJ 它的被接受的答案基本上使用了我建议的方法 [getResource("MyClass.class").getPath()] ...但它并没有直接回答这是否是唯一或最好的方法。 - Jonik
5个回答

4
我知道这篇文章很老,但它是谷歌搜索结果的首选,并且在这里没有令我满意的答案。以下是我编写的一些代码,在我的情况下运行良好。当然有一个警告,可能它没有从磁盘加载,但它对此进行了考虑,并在这种情况下返回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);
    }
}

3

您为何认为有一个类文件在其自己的目录中?

一个类可以是:

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

您可以获取用于创建类本身的URL,并且如果该URL以file://开头,则可以获取剩余部分...但这并不适用于所有类。


是的,我认为这基本上就是我的同事提出替代方案时所指的...但是确实有一些情况,你知道类将始终从磁盘文件加载-所以如果是这种情况,我在问题中提出的建议是否是最清晰的方式?不过感谢您提供有关检查URL开头的提示! - Jonik
是的,如果你加入一个检查文件协议的方式,那你所做的看起来对我来说是最清晰的。 - Jon Skeet

2
如果资源与.class文件在同一文件夹中,则可以通过类路径访问该资源,并可直接通过getResourceAsStream加载。
this.getClass().getResourceAsStream( "filename.xml" )

如前所述,类本身可以在远程加载或在没有适当“路径”的地方加载(例如从jar文件中)


1
是的,当然;我正在寻找获取“目录”路径的最简单方法(因为我们正在从那里读取多个资源文件)。 - Jonik
你是想在那个点上从类路径中读取所有例如“.xml”文件,而不需要事先知道它们的名称吗? - Dan Fleet
1
是的,那基本上就是重点了。(虽然在这种情况下我们基本上知道文件名,但我只想动态地读取文件,以避免在某个地方列出它们或类似的事情。) - Jonik

1

我同意你的同事的观点,Java类加载器并不是为处理这种用例而设计的。Sun Facelets使用了类似的策略,假设URL可以映射到文件,但这并不美观。我同意Jon的评论,根据你的部署前提,你的getResource解决方案可能是最清晰的。既然你问它是否是唯一的方法,我也会提供getClass().getProtectionDomain().getCodeSource().getLocation(),它应该是类加载器实际从中加载类的URL(你需要添加你的类包的子目录)。这种策略基于相同的URL-to-File假设,因此在这方面并没有更好的解决方案。我想不出其他通用的解决方案。

注意,getResource返回编码的URL,这意味着你不应该直接使用getPath()。特别地,空格会导致问题,虽然如果你控制你的环境则可能不会产生问题。考虑使用new File(URL.toURI())。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接