# 记一次类加载时机的坑 写了一个导出报表的工具,工具见 [common-utils](https://github.com/c-rainstorm/common-utils) 项目。其中需要对格式化的实现类进行自动注册到工厂,但是第一版写出来并没有按照实际设想的方式运行,具体看正文。 --- - [记一次类加载时机的坑](#%e8%ae%b0%e4%b8%80%e6%ac%a1%e7%b1%bb%e5%8a%a0%e8%bd%bd%e6%97%b6%e6%9c%ba%e7%9a%84%e5%9d%91) - [自动注册工厂[Don't work]](#%e8%87%aa%e5%8a%a8%e6%b3%a8%e5%86%8c%e5%b7%a5%e5%8e%82dont-work) - [自动注册工厂](#%e8%87%aa%e5%8a%a8%e6%b3%a8%e5%86%8c%e5%b7%a5%e5%8e%82) - [参考](#%e5%8f%82%e8%80%83) --- ## 自动注册工厂[Don't work] 工厂类 ```Java @Slf4j public class FieldFormatterFactory { private static Map formatterCacheMap = new HashMap<>(32); public static FieldFormatter get(@NonNull Class kls) { if (formatterCacheMap.containsKey(kls)) { return formatterCacheMap.get(kls); } return formatterCacheMap.get(Object.class); } public synchronized static void register(@NonNull Class kls, @NonNull FormatterHelper fieldFormatter) { if (formatterCacheMap.containsKey(kls)) { throw new FieldFormatterDuplicateException(String.format("valueType: %s, exist: %s[%s], new: %s[%s]", kls.getName(), formatterCacheMap.get(kls).getClass().getName(), formatterCacheMap.get(kls).getClass().getClassLoader(), fieldFormatter.getClass().getName(), fieldFormatter.getClass().getClassLoader())); } formatterCacheMap.put(kls, fieldFormatter); log.info(String.format("valueType: %s, new: %s[%s]", kls.getName(), fieldFormatter.getClass().getName(), fieldFormatter.getClass().getClassLoader())); } } ``` formatter 实现类 ```java public class DefaultFieldFormatter extends AbstractFieldFormatter { static{ FieldFormatterFactory.register(Object.class, new DefaultFieldFormatter()); } @Override public Set getValueTypes() { Set kls = new HashSet<>(); kls.add(Object.class); return kls; } @Override public String apply(ColMata colMata, Object o) { return ObjectUtils.defaultIfNull(o, "").toString(); } } ``` 最初设计时,是希望 Formatter 实现类在加载的时候能自动执行 static 代码块,将自己注册到工厂里,但是运行单元测试时,工厂内并没有实现类,导致了空指针。 因为注册方法如果被调用是会有日志输出的,但是实际上没有,所以判断是类的 static 代码块没有执行 ## 自动注册工厂 工厂,通过反射扫描指定的类 ```java @Slf4j public class FieldFormatterFactory { private static Map formatterCacheMap = new HashMap<>(32); static { Reflections reflections = new Reflections(FieldFormatterFactory.class.getPackage().getName()); Set> classes = reflections.getSubTypesOf(FormatterHelper.class); classes.forEach(formatter -> { try { if (Modifier.isAbstract(formatter.getModifiers())) { log.info("{} is abstract class, skipped.", formatter.getName()); return; } FormatterHelper formatterHelper = formatter.newInstance(); Set klsSet = formatterHelper.getValueTypes(); klsSet.forEach(valueType -> { register(valueType, formatterHelper); }); } catch (Exception e) { log.error(e.getMessage(), e); } }); } public static FieldFormatter get(@NonNull Class kls) { if (formatterCacheMap.containsKey(kls)) { return formatterCacheMap.get(kls); } return formatterCacheMap.get(Object.class); } public synchronized static void register(@NonNull Class kls, @NonNull FormatterHelper fieldFormatter) { if (formatterCacheMap.containsKey(kls)) { throw new FieldFormatterDuplicateException(String.format("valueType: %s, exist: %s[%s], new: %s[%s]", kls.getName(), formatterCacheMap.get(kls).getClass().getName(), formatterCacheMap.get(kls).getClass().getClassLoader(), fieldFormatter.getClass().getName(), fieldFormatter.getClass().getClassLoader())); } formatterCacheMap.put(kls, fieldFormatter); log.info(String.format("valueType: %s, new: %s[%s]", kls.getName(), fieldFormatter.getClass().getName(), fieldFormatter.getClass().getClassLoader())); } } ``` ```java public class DefaultFieldFormatter extends AbstractFieldFormatter { @Override public Set getValueTypes() { Set kls = new HashSet<>(); kls.add(Object.class); return kls; } @Override public String apply(ColMata colMata, Object o) { return ObjectUtils.defaultIfNull(o, "").toString(); } } ``` ## 参考 1. [解析 Java 类和对象的初始化过程](https://www.ibm.com/developerworks/cn/java/j-lo-clobj-init/index.html) 1. [深入理解Java虚拟机]()