Как предотвратить повторную загрузку файла конфигурации? Держать его статически не работает
Я пишу библиотеку на основе тегов, которая при обнаружении первого тега загружает некоторую конфигурацию (начиная с текстового файла, заполненного свойствами).
Объект конфигурации хранится статически непосредственно в каждом Taglet
объект, но кажется, что они собирают мусор, а затем порождают javadoc.exe
в последующем теге, заставляя конфигурацию перезагружаться снова и снова.
Я правильно понимаю, и есть ли способ обойти это? Как сделать так, чтобы конфигурация загружалась только один раз?
Благодарю.
ОБНОВИТЬ
Как уже упоминалось в комментариях, нет, это не влияет на производительность или правильность. поскольку javadoc.exe
используется одним человеком на одной машине, производительность не является большой проблемой.
Тем не менее, он загромождает журнал каждый раз при загрузке конфигурации (по крайней мере, пять раз за javadoc.exe
запустить), и он делает некоторые умеренно тяжелые вещи, включая загрузку package-list
с нескольких сайтов, загрузка и анализ файлов шаблонов, а также куча других файловых обработок. Если есть какой-либо способ предотвратить это много раз за один запуск JavaDoc, я бы хотел.
У меня нет опыта работы с многопоточностью, поэтому я могу ошибаться... но как насчет настройки потока демона, который не выполняет ничего, кроме загрузки конфигурации, а затем сохраняет все статически? Этот ответ предполагает, что поток демонов на основе ввода / вывода - плохая идея, но я думаю, что это означает те, которые выполняют текущий ввод / вывод.
(Я не уверен, что это должно быть что-то, что должно быть вручную запущено и остановлено, или если возможно, чтобы сам процесс запустил поток демона... Я собираюсь прочитать главы по параллелизму в Эффективной Java Блоха...)
1 ответ
Если два класса загружаются разными ClassLoader
Если родительские и дочерние отношения не обмениваются данными, обычные конструкции языка Java не работают. Если вы можете получить руки на Class
объект или экземпляр, вы должны получить к ним доступ через Reflection, даже если это один и тот же класс, только что загруженный разными загрузчиками.
Кроме того, передача данных через переменные кучи не будет работать как ClassLoader
s устанавливают свое собственное "пространство имен", следовательно, класс загружается двумя разными загрузчиками, создавая два разных 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…