Как передать параметр в статический блок инициализации

Я хочу загрузить пары ключ / значение из файла (файл Excel, используя Apache poi) в статическую карту, которая будет использоваться в качестве таблицы поиска. После загрузки таблица не изменится.

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.

Есть ли лучший подход к этому?

7 ответов

Решение

Вы не можете передавать информацию в статические блоки инициализации - они должны работать изолированно. Поскольку поток, который вы планируете передать, должен быть известен до начала выполнения программы, вероятно, ваш LookupTable должен быть в состоянии найти это тоже. Например, это может быть какая-то утилита конфигурирования, которая предоставляет вам поток. Затем вы можете написать свой инициализатор следующим образом:

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

Предположительно, в системе есть класс, который может получить ваш поток Excel из известного ему источника. Источник не должен быть жестко запрограммирован: он может считывать местоположение из файла конфигурации или получать данные из предопределенного сетевого расположения на вашем сервере. Во всех случаях процесс получения потока Excel должен где-то "снизиться", в том смысле, что что-то в вашей системе должно быть в состоянии найти его без дополнительных параметров.

Все, что вы используете, должно быть доступно при запуске. Насколько я знаю, ваши варианты:

  1. Жесткий код пути. Это плохо по понятным причинам.
  2. Статическая переменная или статический метод. Это представляет собой проблему курицы и яйца; в конечном итоге это становится жестко запрограммированным, но, по крайней мере, вы можете сделать поиск с static метод.
  3. Используйте переменную, либо Java, либо Environment. Итак, вы бы использовали что-то System.getProperty("filename", "/default/filename"), Лучше, потому что это, по крайней мере, настраивается с помощью среды или -D параметры при запуске JVM.
  4. Использовать ClassLoadergetResource* методы. Это, вероятно, правильный ответ. В частности, вы, вероятно, захотите использовать getResourceAsStream() метод в контексте текущего потока ClassLoaderThread.currentThread().getContextClassLoader(), (Так, Thread.currentThread().getContextClassLoader().getResourceAsStream("filename") в общей сложности.) ClassLoader затем найдет ваш ресурс для вас (если вы положите его где-то в своем уме CLASSPATH).

Да, есть лучший подход, используйте шаблон проектирования Factory для инициализации объекта, прежде чем использовать его:

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

Это не прямо отвечает на ваш вопрос, но я не понимаю, почему map должен быть статичным. Вы могли бы изменить map нестатический и изменить конструктор на public LookupTable(File file) {...fill map...}, Тогда у вас может быть даже много экземпляров этого класса, если у вас разные файлы Excel; сейчас это может быть не так, но это может "заглянуть в будущее" вашего кода.

Это, вероятно, случай использования ленивой загрузки карты.

Но вам нужно будет установить 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 например.

Попробуйте использовать System.getProperty() и передать параметр с ключом -D в командной строке.

static String prop;

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

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

    System.out.println(prop);
}

Я бы предложил метод синглтоновых перечислений, если он подходит для вашего случая.

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 заранее инициализирован.

Другие вопросы по тегам