Как предотвратить повторную загрузку файла конфигурации? Держать его статически не работает

Я пишу библиотеку на основе тегов, которая при обнаружении первого тега загружает некоторую конфигурацию (начиная с текстового файла, заполненного свойствами).

Объект конфигурации хранится статически непосредственно в каждом Taglet объект, но кажется, что они собирают мусор, а затем порождают javadoc.exe в последующем теге, заставляя конфигурацию перезагружаться снова и снова.

Я правильно понимаю, и есть ли способ обойти это? Как сделать так, чтобы конфигурация загружалась только один раз?

Благодарю.


ОБНОВИТЬ

Как уже упоминалось в комментариях, нет, это не влияет на производительность или правильность. поскольку javadoc.exe используется одним человеком на одной машине, производительность не является большой проблемой.

Тем не менее, он загромождает журнал каждый раз при загрузке конфигурации (по крайней мере, пять раз за javadoc.exe запустить), и он делает некоторые умеренно тяжелые вещи, включая загрузку package-listс нескольких сайтов, загрузка и анализ файлов шаблонов, а также куча других файловых обработок. Если есть какой-либо способ предотвратить это много раз за один запуск JavaDoc, я бы хотел.

У меня нет опыта работы с многопоточностью, поэтому я могу ошибаться... но как насчет настройки потока демона, который не выполняет ничего, кроме загрузки конфигурации, а затем сохраняет все статически? Этот ответ предполагает, что поток демонов на основе ввода / вывода - плохая идея, но я думаю, что это означает те, которые выполняют текущий ввод / вывод.

(Я не уверен, что это должно быть что-то, что должно быть вручную запущено и остановлено, или если возможно, чтобы сам процесс запустил поток демона... Я собираюсь прочитать главы по параллелизму в Эффективной Java Блоха...)

1 ответ

Если два класса загружаются разными ClassLoaderЕсли родительские и дочерние отношения не обмениваются данными, обычные конструкции языка Java не работают. Если вы можете получить руки на Class объект или экземпляр, вы должны получить к ним доступ через Reflection, даже если это один и тот же класс, только что загруженный разными загрузчиками.

Кроме того, передача данных через переменные кучи не будет работать как ClassLoaders устанавливают свое собственное "пространство имен", следовательно, класс загружается двумя разными загрузчиками, создавая два разных Class объекты будут иметь свои отличные копии static переменные также. Вам нужно хранилище, которое не зависит от ваших собственных классов.

К счастью, это хранилище существует в контексте Taglets. register метод получает Map содержащий все ранее зарегистрированные Taglets. Но помимо того, что вы должны использовать отражение, а не instanceof или же Class Сравнение для поиска ваших "друзей" Taglets, есть еще одно препятствие: реализация JavaDoc обернет ваши Taglets в другой объект.

Собрав все это вместе, вы можете реализовать логику поиска и обмена в базовом классе ваших Taglets и позволить register Метод Taglets называют его:

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.Properties;

public abstract class Base
{
  static Properties CONFIG=new Properties();

  static void initProperties(Map<?, ?> fromTagManager) {
    String className=Base.class.getName();
    for(Object o: fromTagManager.values()) {
      o=extractTagLet(o);
      if(o==null) continue;
      for(Class<?> cl=o.getClass(); cl!=null; cl=cl.getSuperclass())
        if(cl.getName().equals(className) && initFromPrevious(cl)) return;
    }
    // not found, first initialization
    try {
      CONFIG.load(Base.class.getResourceAsStream("config.properties"));
    } catch(IOException ex) {
      throw new ExceptionInInitializerError(ex);
    }
  }

  private static Object extractTagLet(Object o) {
    if(!o.getClass().getSimpleName().equals("LegacyTaglet"))
      return o;
    try {
      Field f=o.getClass().getDeclaredField("legacyTaglet");
      f.setAccessible(true);
      return f.get(o);
    } catch(NoSuchFieldException | IllegalAccessException ex) {
      ex.printStackTrace();
    }
    return null;
  }

  private static boolean initFromPrevious(Class<?> cl) {
    // this is the same class but loaded via a different ClassLoader
    try {
      Field f=cl.getDeclaredField("CONFIG");
      f.setAccessible(true);
      CONFIG=(Properties)f.get(null);
      return true;
    } catch(NoSuchFieldException | IllegalAccessException ex) {
      return false;
    }
  }
}

Тогда Taglet будет реализован так:

import java.util.Map;
import com.sun.javadoc.Tag;
import com.sun.tools.doclets.Taglet;

public class ExampleTaglet extends Base implements Taglet {
  @SuppressWarnings("unchecked")
  public static void register(@SuppressWarnings("rawtypes") Map map) {
    initProperties(map);
    final ExampleTaglet taglet = new ExampleTaglet();
    final String name = taglet.getName();
    map.remove(name);// must ensure new Taglet is the last one (LinkedHashMap)
    map.put(name, taglet);
  }

  // implement the Taglet interface below…
Другие вопросы по тегам