我正在编写taglet-based library,当找到第一个标记时,会加载一些配置(从填充了属性的文本文件开始)。
配置对象直接静态保存在每个Taglet
对象中,但似乎它们被垃圾收集,然后由javadoc.exe
在后续标记中重新生成,导致配置重新加载再一次。
我是否正确地理解了这一点,并且有办法吗?我怎样才能使配置加载一次?
感谢。
更新
如评论中所述,不,这不会影响性能或正确性。由于单个人在一台计算机上使用javadoc.exe
,因此性能不是问题。
然而,每次加载配置时它都会使日志变得混乱(每javadoc.exe
次运行至少五次),它会做一些中等重量的事情,包括从多个网站加载package-list
,加载和解析模板文件,以及一堆其他文件处理。如果有任何方法可以在单个JavaDoc运行中多次阻止这种情况发生,我想。
我没有使用多线程的经验,所以我可能有完全错误...但是如何设置一个守护程序线程除了加载配置之外什么都不做,然后静态保存它? This answer表明基于I / O的守护程序线程是一个坏主意,但我认为这意味着那些正在进行I / O的程序。
(我不确定这是否应该是手动启动和停止的,或者是否可能让进程本身启动守护程序线程...我将阅读并发性布洛赫的有效Java中的章节......)
答案 0 :(得分:0)
如果由不具有父子关系的不同ClassLoader
加载的两个类必须共享数据,则普通的Java语言结构不起作用。如果您可以使用Class
对象或实例,则必须通过Reflection访问它们,即使它是由不同加载器加载的同一类。
此外,通过堆变量传递数据将不起作用,因为ClassLoader
都建立了自己的“命名空间”,因此由两个不同的加载器加载的类创建两个不同的Class
对象将具有其独特的static
个变量的副本。您需要一个独立于您自己的类的存储空间。
幸运的是,该存储存在于Taglet的上下文中。 register
方法接收包含所有先前注册的Taglet的Map
。但除了你必须使用Reflection而不是instanceof
或Class
比较来找到你的“朋友”Taglet之外,还有另一个障碍:JavaDoc实现会将你的Taglet包装在另一个对象中。
总而言之,您可以将find-and-share逻辑实现到Taglet的基类中,并让Taglet的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…