6

我想要做的是将文件(使用 Apache poi 的 excel 文件)中的键/值对加载到将用作查找表的静态映射中。一旦加载表将不会改变。

public final class LookupTable
{  
   private final static Map<String, String> map;  
   static {
     map = new HashMap<String, String>();
     // should do initialization here
     // InputStream is = new FileInputStream(new File("pathToFile"));
     // not sure how to pass pathToFile without hardcoding it?
   }

   private LookupTable() {
   }

  public static void loadTable(InputStream is) {
    // read table from file
    // load it into map
    map.put("regex", "value");
  }

  public static String getValue(String key) {
    return map.get(key);
  }
}

理想情况下,我想在静态初始化块中加载地图,但是如何在不对其进行硬编码的情况下传递流呢?我看到使用 loadTable 静态方法的问题是在调用其他静态方法之前可能不会调用它。

// LookupTable.loadTable(stream);  
LookupTable.getValue("regex"); // null since map was never populated.

有更好的方法吗?

4

7 回答 7

4

您使用的任何东西都必须在启动时可以访问。据我所知,您的选择是:

  1. 硬编码路径。这很糟糕,原因很明显。
  2. 静态变量或静态方法。这提出了一个先有鸡还是先有蛋的问题。最终它会被硬编码,但至少您可以使用static方法进行搜索。
  3. 使用变量,Java 或环境。所以,你会使用一些东西System.getProperty("filename", "/default/filename")。更好,因为它至少可以-D在 JVM 启动时使用环境或参数进行定制。
  4. 使用ClassLoader getResource*方法。这可能是正确的答案。具体来说,您可能希望getResourceAsStream()在当前线程的 context 上使用该方法ClassLoader Thread.currentThread().getContextClassLoader()。(所以,Thread.currentThread().getContextClassLoader().getResourceAsStream("filename")总的来说。)然后ClassLoader会为你找到你的资源(只要你把它放在你的CLASSPATH.
于 2013-04-03T15:21:41.307 回答
2

是的,有一个更好的方法,在你必须使用它之前使用工厂设计模式来初始化对象:

http://www.oodesign.com/factory-pattern.html

于 2013-04-03T15:16:20.717 回答
1

您不能将信息传递到静态初始化块中——它们应该独立工作。由于您计划通过的流需要在程序开始执行之前知道,因此您LookupTable应该也可以找到它。例如,这可能是某种为您提供流的配置实用程序。然后你可以这样写你的初始化程序:

static {
    InputStream exelStream = MyConfigUtil.getExcelStreamForLookup();
    loadTable(exelStream);
}

据推测,系统中有一个类可以从已知的源获取您的 Excel 流。源不需要硬编码:它可以从配置文件中读取位置,或者从服务器上预定义的网络位置接收数据。在所有情况下,获取 Excel 流的过程都必须在某个地方“触底”,因为系统中的某些东西需要能够在没有其他参数的情况下找到它。

于 2013-04-03T15:17:43.153 回答
1

这不是直接回答您的问题,但我不明白为什么map必须是静态的。您可以更改map为非静态并将构造函数更改为public LookupTable(File file) {...fill map...}. 如果您有不同的 excel 文件,您甚至可以拥有该类的许多实例;现在可能不是这种情况,但它会“面向未来”您的代码。

于 2013-04-03T15:29:06.263 回答
0

这可能是使用延迟加载地图的情况。

但是您需要在第一次inputFileName调用之前设置。getValue()这将在应用程序的初始化代码中完成。(或者你可以有一个静态方法来设置它。)

这指出了延迟加载的优势。getValue()在您第一次调用之前,您不必提供可用的文件名。使用静态初始化程序,您必须将文件名存储在类之外的某个位置,以便在类加载时(但在初始化静态字段之后)可以使用它来加载数据。

 public static String inputFileName = null;

 public static String getValue(String key) {
    if (map == null) {
        map = = new HashMap<String, String>();
        // open the file using 'inputFileName'
        loadTable(InputStream is);
    }
    return map.get(key);
  }

如果您的代码是多线程的,请告诉我,我会评论同步问题。

备用

您还可以使用 Spring 来注入地图并在其他一些类中构建它——MapBuilder例如。

于 2013-04-03T15:16:32.820 回答
0

尝试使用 System.getProperty() 并在命令行中使用 -D 传递参数。

static String prop;

static {
    prop = System.getProperty("java.home");
}

public static void main(String... args) {

    System.out.println(prop);
}
于 2013-04-03T15:23:08.723 回答
0

如果适合您的情况,我会建议使用单例枚举方法。

public enum LookupTable {

    INSTANCE(FileManager.getFileName());

    LookupTable(String fileName){
        props = new HashMap<String,String>();
        //Read from excel and fill the hashmap
    }

    private final Map<String, String> props;

    public String getMapValue(String key){
        return props.get(key);
    }   
}

可以调用哪个

LookupTable.INSTANCE.getMapValue("mykey");

这将按顺序调用这些方法

  • 从文件管理器类中获取文件名,该类根据您的需要进行参数化
  • 调用构造函数(它是私有的)并从 excel 文件中加载属性
  • getMapValue 获取键并返回

后续调用 LookupTable.INSTANCE.getMapValue("mysecondkey") 只会调用 getMapValue,因为 INSTANCE 是预先初始化的。

于 2013-04-03T16:11:02.427 回答