由于多种原因,您的方法(据我所知,它是尝试以编程方式更改生成属性文件的源文件)在部署时不起作用。
首先是源文件在运行时通常不可用:您的 jar 文件包含运行应用程序所需的类文件和其他资源,但通常不包含源代码(并且不希望将其包含在应用程序中, 一般来说)。
其次,您试图从传递给Paths.get(..) 的相对路径中找到此文件。这将相对于工作目录进行解析,工作目录本质上是任意的(基本上是“应用程序的运行位置”)。因此,即使源代码在运行时可用,这也不是找到它的可靠方法。 (我的猜测是你的调试器运行时工作目录偶然设置为src的父目录,但是当你运行jar文件时,工作目录最有可能的位置是jar文件所在的目录。但是这只是一个猜测:它实际上取决于您的 IDE、调试器等的配置。)
第三,可能也是最重要的一点,即使代码确实找到了源文件并重写了它,这也是它所能做的。下次您从 jar 文件执行应用程序时,它不会神奇地知道有一个新版本的源代码需要编译,然后将生成的类文件合并到 jar 文件中。因此,您还必须包含代码来编译源代码的新版本(您将在哪里获得编译器?AFAIK 并非所有 Java 运行时都包含编译器),然后以编程方式将新的类文件插入到 jar 文件中(您甚至如何弄清楚 jar 文件在哪里:这当然不是微不足道的,我认为它不能以可靠的方式完成)。如果当前用户没有权限写入包含 jar 的目录(这是一种非常常见的情况..)怎么办?
加载和保存启动配置值的常用方法是使用java.util.Properties API。您需要一个外部位置来存储属性文件,您可以确定它存在于用户的计算机上:一种方便的方法是在用户的主目录中创建一个特定于应用程序的目录。可以通过System.getProperty("user.home"); 访问用户的主目录(系统属性user.home 是guaranteed to exist 之一。)。
我建议使用单独的类来管理配置属性。例如:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.Properties;
public class PropertiesAccessor {
private static final Path USER_HOME = Paths.get(System.getProperty("user.home"));
private Properties props ;
private Path path ;
public PropertiesAccessor(String relativePath) {
path = USER_HOME.resolve(relativePath);
props = new Properties();
if (Files.exists(path)) {
try {
props.load(Files.newBufferedReader(path));
} catch (IOException exc) {
System.err.println("Warning: could not load properties file. Using defaults.");
exc.printStackTrace(System.err);
loadDefaults();
}
} else {
loadDefaults();
}
}
public Boolean getBooleanValue(String key) {
String value = props.getProperty(key);
return value == null ? null : Boolean.valueOf(value) ;
}
public void updateBooleanValue(String key, boolean value) {
props.setProperty(key, Boolean.toString(value));
}
public void writeProperties() throws IOException {
if (! Files.exists(path)) {
Files.createDirectories(path.getParent());
Files.createFile(path);
}
props.store(Files.newBufferedWriter(path), "Properties updated "+LocalDateTime.now());
}
private final void loadDefaults() {
// in real life, you might keep a default properties file bundled with
// the application and read that here, e.g.
// props.load(getClass().getResourceAsStream("/default-startup.properties"));
props.setProperty("config.value1", "true");
props.setProperty("config.value2", "false");
}
}
现在您可以在您的应用程序中使用它。只需在init() 方法中加载属性并将它们保存回stop() 方法中。 请注意,执行此操作将在您的主目录中创建一个名为 .myApp 的目录,并在其中创建一个名为 startup.properties 的文件。
import java.io.IOException;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class StartupPropertiesExample extends Application {
private PropertiesAccessor config ;
private CheckBox value1 ;
private CheckBox value2 ;
@Override
public void init() {
config = new PropertiesAccessor(".myApp/startup.properties");
}
@Override
public void start(Stage primaryStage) {
value1 = new CheckBox("Value 1");
value2 = new CheckBox("Value 2");
value1.setSelected(config.getBooleanValue("config.value1"));
value2.setSelected(config.getBooleanValue("config.value2"));
Button exit = new Button("Exit");
exit.setOnAction(e -> Platform.exit());
VBox root = new VBox(10, value1, value2, exit);
root.setPadding(new Insets(10));
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
@Override
public void stop() {
config.updateBooleanValue("config.value1", value1.isSelected());
config.updateBooleanValue("config.value2", value2.isSelected());
try {
config.writeProperties();
} catch (IOException exc) {
System.err.println("Warning: could not save properties");
exc.printStackTrace(System.err);
}
}
public static void main(String[] args) {
launch(args);
}
}