读取JAR文件外部的属性文件

152

我有一个JAR文件,里面存档了我所有的代码以供运行。在每次运行之前,我必须访问一个需要更改/编辑的属性文件。我想把这个属性文件保存在与JAR文件相同的目录中。是否有一种方法可以告诉Java从那个目录中获取属性文件?

注意:我不想把属性文件保存在主目录中,也不想通过命令行参数传递属性文件的路径。


5
我需要将属性文件保留在jar目录中的原因是,当整个目录(包括jar和属性文件)被复制到另一台机器上并运行时,最好将它们放在一起。 - Neil
1
如果我强制用户传递属性文件路径,那么每次他从不同的机器上运行批处理文件时都需要更改它。 - Neil
9个回答

169

所以,你想把与主/可运行jar文件同一文件夹下的.properties文件作为文件而不是主/可运行jar文件的资源来处理。在这种情况下,我的解决方案如下:

首先:你的程序文件结构应该像这样(假设你的主程序是main.jar,它的主要属性文件是main.properties):

./ - the root of your program
 |__ main.jar
 |__ main.properties

使用这种架构,你可以在任何文本编辑器中修改main.properties文件中的任何属性,在main.jar运行时进行修改(取决于程序的当前状态),因为它只是一个基于文本的文件。例如,你的main.properties文件可能包含以下内容:

app.version=1.0.0.0
app.name=Hello

因此,当您从其根/基本文件夹运行主程序时,通常会像这样运行:

java -jar ./main.jar

或者,直接:

java -jar main.jar

在你的 main.jar 中,你需要为每个在 main.properties 文件中找到的属性创建一些实用方法;比如说,app.version 属性将会有以下 getAppVersion() 方法:

/**
 * Gets the app.version property value from
 * the ./main.properties file of the base folder
 *
 * @return app.version string
 * @throws IOException
 */

import java.util.Properties;

public static String getAppVersion() throws IOException{

    String versionString = null;

    //to load application's properties, we use this class
    Properties mainProperties = new Properties();

    FileInputStream file;

    //the base folder is ./, the root of the main.properties file  
    String path = "./main.properties";

    //load the file handle for main.properties
    file = new FileInputStream(path);

    //load all the properties from this file
    mainProperties.load(file);

    //we have loaded the properties, so close the file handle
    file.close();

    //retrieve the property we are intrested, the app.version
    versionString = mainProperties.getProperty("app.version");

    return versionString;
}
在主程序的任何部分需要 app.version 值时,我们可以按照以下方式调用其方法:
String version = null;
try{
     version = getAppVersion();
}
catch (IOException ioe){
    ioe.printStackTrace();
}

9
这个解决方案可行。感谢您理解精确的要求和详细的代码。我验证了属性文件不在jar文件内,但仍然可以从与jar文件相同的目录访问该文件。这样就不需要硬编码任何绝对路径。现在可以将jar和属性文件复制到任何目录并独立运行。 - Neil
5
如果你从外部执行命令,例如{{java -jar build/main.jar}},文件将找不到。 @eee,你有解决方法吗? - Darian
1
@Darian 这里没有什么需要修复的; 它只按设计工作,在文件组织架构中描述的那样,jar 和属性文件必须在同一 ./ 根文件夹(相同的目录级别)上。 (根据原始帖子设置的要求) - ecle
@Darian,如果你想执行 java -jar build/main.jar,你需要将属性文件也放在 build 文件夹中,这样它就与 jar 包处于同一目录级别。 - ecle
9
感谢您的回复@eee,问题是我不知道用户会在哪里执行java -jar path/to/jar/file命令。但是我在另一个问题中找到了解决方案:String path = ClassLoader.getSystemClassLoader().getResource(".").getPath() + "/main.properties"; - Darian
显示剩余3条评论

52

我通过其他方式实现了它。

Properties prop = new Properties();
    try {

        File jarPath=new File(MyClass.class.getProtectionDomain().getCodeSource().getLocation().getPath());
        String propertiesPath=jarPath.getParentFile().getAbsolutePath();
        System.out.println(" propertiesPath-"+propertiesPath);
        prop.load(new FileInputStream(propertiesPath+"/importer.properties"));
    } catch (IOException e1) {
        e1.printStackTrace();
    }
  1. 获取Jar文件路径。
  2. 获取该文件的父文件夹。
  3. 将该路径与您的属性文件名一起用于InputStreamPath。

我不得不放弃getParentFile()部分,所以我使用了:String propertiesPath=jarPath.getAbsolutePath(); 但这完全取决于文件的位置。 - MobileMon
4
只需将“jarPath.getParentFile().getAbsolutePath();”替换为“jarPath.getParent()”,就可以完美运行。 - StackAddict
1
我的情况也是一样的问题,但我有一个基于Spring的项目。如何告诉Spring文件在jar文件旁边?有什么想法吗? - Mubasher

3

从jar文件访问文件目录时,通常会遇到问题。在jar文件中提供classpath的方法非常有限。相反,尝试使用bat文件或sh文件启动程序,这样你可以按照自己的方式指定classpath,并引用系统上任何位置的文件夹。

此外,请查看我在making .exe file for java project containing sqlite这个问题上的回答。


3
我有一个类似的情况:想让我的*.jar文件访问与该*.jar文件相邻的目录中的文件。请参考此答案

我的文件结构是:

./ - the root of your program
|__ *.jar
|__ dir-next-to-jar/some.txt

我可以使用以下代码将文件(例如some.txt)加载到 *.jar 文件中的 InputStream 中:

InputStream stream = null;
    try{
        stream = ThisClassName.class.getClass().getResourceAsStream("/dir-next-to-jar/some.txt");
    }
    catch(Exception e) {
        System.out.print("error file to stream: ");
        System.out.println(e.getMessage());
    }

然后,您可以随意处理 stream

1
File parentFile = new File(".");
String parentPath = file.getCanonicalPath();
File resourceFile = new File(parentPath+File.seperator+"<your config file>");

3
请在您的回答中添加一些解释。 - kk.

1
这对我很有用。从当前目录加载您的属性文件。 注意:方法Properties#load使用ISO-8859-1编码
Properties properties = new Properties();
properties.load(new FileReader(new File(".").getCanonicalPath() + File.separator + "java.properties"));
properties.forEach((k, v) -> {
            System.out.println(k + " : " + v);
        });

请确保 java.properties 文件在当前目录下。你可以编写一个小的启动脚本,在执行之前切换到正确的目录,例如:

#! /bin/bash
scriptdir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 
cd $scriptdir
java -jar MyExecutable.jar
cd -

在您的项目中,只需将java.properties文件放置在项目根目录中,以便使此代码能够从IDE中运行。

1

如果你提到 .getPath(),它会返回Jar的路径,我猜你需要它的父级来引用与jar放置在一起的所有其他配置文件。这段代码在Windows上可以工作。将代码添加到主类中。

File jarDir = new File(MyAppName.class.getProtectionDomain().getCodeSource().getLocation().getPath());
String jarDirpath = jarDir.getParent();

System.out.println(jarDirpath);

0

我有一个使用classpath或外部配置文件log4j2.properties的示例

package org.mmartin.app1;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.LogManager;


public class App1 {
    private static Logger logger=null; 
    private static final String LOG_PROPERTIES_FILE = "config/log4j2.properties";
    private static final String  CONFIG_PROPERTIES_FILE = "config/config.properties";

    private Properties properties= new Properties();

    public App1() {
        System.out.println("--Logger intialized with classpath properties file--");
        intializeLogger1();
        testLogging();
        System.out.println("--Logger intialized with external file--");
        intializeLogger2();
        testLogging();
    }




    public void readProperties()  {
        InputStream input = null;
        try {
            input = new FileInputStream(CONFIG_PROPERTIES_FILE);
            this.properties.load(input);
        } catch (IOException e) {
            logger.error("Unable to read the config.properties file.",e);
            System.exit(1);
        }
    }

    public void printProperties() {
        this.properties.list(System.out);
    }

    public void testLogging() {
        logger.debug("This is a debug message");
        logger.info("This is an info message");
        logger.warn("This is a warn message");
        logger.error("This is an error message");
        logger.fatal("This is a fatal message");
        logger.info("Logger's name: "+logger.getName());
    }


    private void intializeLogger1() {
        logger = LogManager.getLogger(App1.class);
    }
    private void intializeLogger2() {
        LoggerContext context = (org.apache.logging.log4j.core.LoggerContext) LogManager.getContext(false);
        File file = new File(LOG_PROPERTIES_FILE);
        // this will force a reconfiguration
        context.setConfigLocation(file.toURI());
        logger = context.getLogger(App1.class.getName());
    }

    public static void main(String[] args) {
        App1 app1 = new App1();
        app1.readProperties();
        app1.printProperties();
    }
}


--Logger intialized with classpath properties file--
[DEBUG] 2018-08-27 10:35:14.510 [main] App1 - This is a debug message
[INFO ] 2018-08-27 10:35:14.513 [main] App1 - This is an info message
[WARN ] 2018-08-27 10:35:14.513 [main] App1 - This is a warn message
[ERROR] 2018-08-27 10:35:14.513 [main] App1 - This is an error message
[FATAL] 2018-08-27 10:35:14.513 [main] App1 - This is a fatal message
[INFO ] 2018-08-27 10:35:14.514 [main] App1 - Logger's name: org.mmartin.app1.App1
--Logger intialized with external file--
[DEBUG] 2018-08-27 10:35:14.524 [main] App1 - This is a debug message
[INFO ] 2018-08-27 10:35:14.525 [main] App1 - This is an info message
[WARN ] 2018-08-27 10:35:14.525 [main] App1 - This is a warn message
[ERROR] 2018-08-27 10:35:14.525 [main] App1 - This is an error message
[FATAL] 2018-08-27 10:35:14.525 [main] App1 - This is a fatal message
[INFO ] 2018-08-27 10:35:14.525 [main] App1 - Logger's name: org.mmartin.app1.App1
-- listing properties --
dbpassword=password
database=localhost
dbuser=user

0
一种方法是在META-INF/MANIFEST.MF文件中使用以下条目将当前目录添加到JAR的类路径中:
Class-Path: .

然后,以下代码将无缝加载"system.properties"文件:
ResourceBundle systemPropertiesBundle = ResourceBundle.getBundle("system");

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