0

我正在编写一个基于 taglet 的库,当找到第一个 taglet 时,它会加载一些配置(从一个填充属性的文本文件开始)。

配置对象直接静态保存在每个Taglet对象中,但似乎它们正在被垃圾收集,然后javadoc.exe在后续的 taglet 中重新生成,导致配置一次又一次地重新加载。

我是否正确理解这一点,有没有办法解决?我怎样才能使配置只加载一次?

谢谢。


更新

如评论中所述,不,这不会影响性能或正确性。由于javadoc.exe由一个人在一台机器上使用,因此性能不是什么大问题。

但是,每次加载配置时(每次javadoc.exe运行至少 5 次),它都会弄乱日志,并且它会做一些中等繁重的事情,包括package-list从多个网站加载 s,加载和解析模板文件,以及一堆其他文件处理. 如果有任何方法可以防止这种情况在单个 JavaDoc 运行中多次发生,我愿意。

我没有使用多线程的经验,所以我可能完全错了……但是设置一个除了加载配置之外什么都不做的守护线程,然后静态地保持它呢?这个答案表明基于 I/O 的守护线程是一个坏主意,但我认为这意味着那些执行持续 I/O 的线程。

(我不确定这是否应该手动启动和停止,或者进程本身是否有可能启动守护线程......我将阅读 Bloch 的 Effective Java 中的并发章节...... .)

4

1 回答 1

0

如果由不同的没有父子关系的 s 加载的两个类ClassLoader必须共享数据,那么普通的 Java 语言结构不起作用。如果您可以获取Class对象或实例,则必须通过反射访问它们,即使它是由不同加载器加载的同一个类。

此外,通过堆变量传递数据将不起作用,因为两者都ClassLoader建立了自己的“命名空间”,因此由两个不同的加载器加载的类创建两个不同的对象也将具有它们不同的变量Class副本。static您需要一个独立于您自己的类的存储。

值得庆幸的是,该存储存在于 Taglets 的上下文中。该register方法接收一个Map包含所有先前注册的 Taglets。但是除了你必须使用反射而不是instanceof比较Class来找到你的“朋友” Taglets 之外,还有另一个障碍:JavaDoc 实现会将你的 Taglets 包装在另一个对象中。

综上所述,您可以在 Taglets 的基类中实现查找和共享逻辑,并让 Taglets 的register方法调用它:

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…
于 2014-06-02T13:18:42.660 回答