在vert.x中,为什么静态方法运行在静态代码块之前?

In vert.x, why do static methods run before static code blocks?

我有一个 class ConfigFactory,它可以通过 vert.x conf 模块从 JSON 文件中给我一些配置。

public class ConfigFactory {
    private static JsonObject result = new JsonObject();
    static {
        ConfigStoreOptions fileStore = new ConfigStoreOptions()
                .setType("file")
                .setOptional(true)
                .setFormat("json")
                .setConfig(new JsonObject().put("path", "conf/config.json"));
        ConfigRetrieverOptions options = new ConfigRetrieverOptions().addStore(fileStore);
        ConfigRetriever retriever = ConfigRetriever.create(VertxSingleton.VERTX, options);
        retriever.getConfig(ar -> {
            if (ar.failed()) {
                throw new RuntimeException("Config get error! Something went wring during getting the config");
            } else {
                result.mergeIn(ar.result());
            }
        });
    }

    public static JsonObject getHttpConfig() {
        BiFunction<Integer, String, JsonObject> httpConfigFile = (port, host) -> new JsonObject()
                .put("port", port).put("host", host);
        if (!result.isEmpty()) {
            JsonObject http = result.getJsonObject("http");
            return httpConfigFile.apply(http.getInteger("port"), http.getString("host"));
        } else {
            throw new RuntimeException("HTTP Config get error! Something went wring during getting the config");
        }

    }
}

但是在Verticle中,我使用JsonObject httpConfig = ConfigFactory.getHttpConfig();,它会给我异常 HTTP Config get error! Something went wring during getting the config。此时,result为空。

我在静态代码块之前找到了静态方法 getHttpConfig 运行。大约一秒钟,静态代码块将 运行。当时result不为空

你能帮帮我吗?谢谢!

与其说是 vertX 相关问题,不如说是设计问题。静态块在 class 被 class 加载器加载时执行,它基本上会在 class 首次被引用时发生。如果您第一次引用它是在您使用 ConfigFactory.getHttpConfig(); 时,那么 class 将由 class 加载程序加载,执行静态块并使用处理程序调用 retrieveConfig。不幸的是它没有阻止执行(处理程序等待结果)并且您立即调用 class 。因此,为了使其正常工作,您应该在完成初始化后调用 getHttpConfig()。

  • 使静态块同步的一种解决方案是使用 Future 选项。

例如:

Future<JsonObject> futureJson=ConfigRetriever.getConfigAsFuture(retriever);
JsonObject obj=futureJson.get();

当在静态块中调用 future.get() 时,它将等到检索到配置后再继续。这似乎是一个更容易理解的解决方案,但我不会使用它。它是异步的是有原因的,我们不应该改变这种行为。

  • 另一种可能需要更多编码的解决方案是添加一个静态字段以显示它是否处于工作状态。

例如:

private static JsonObject result = new JsonObject();
private static boolean readyToGo;
    static {       
        retriever.getConfig(ar -> {
           ....
            if (ar.failed()) {
             ...
            } else {
                result.mergeIn(ar.result());
                ConfigFactory.readyToGo=true; //Now we are good to go
            }
        });
    }

然后

   public static JsonObject getHttpConfig() {
      if (!readyToGo) throw Exception
    }

添加一个isReady()方法,调用getHttpConfig()方法时,首先调用isReady()。类似于:

while(!ConfigFactory.isReady()){
//wait ;)
}
ConfigFactory.getHttpConfig(); //it will work here

我猜你可以写得更好 ;) 但总体思路是在对象是否准备好使用时保持状态。执行该静态配置并不能保证对象处于就绪状态,因此您应该自己管理该状态。

  • 简单的解决方案是在启动时调用 class 一次,以便它可以初始化(class 加载),然后稍后调用 getHttpConfig() 。它不能保证 100% 的结果,但如果检索器花费太多时间或者即使它从未完成检索配置。