写了一个导出报表的工具,工具见 common-utils 项目。其中需要对格式化的实现类进行自动注册到工厂,但是第一版写出来并没有按照实际设想的方式运行,具体看正文。
工厂类
@Slf4j
public class FieldFormatterFactory {
private static Map<Class, FormatterHelper> formatterCacheMap = new HashMap<>(32);
public static <T> FieldFormatter<T> get(@NonNull Class<T> 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 实现类
public class DefaultFieldFormatter extends AbstractFieldFormatter<Object> {
static{
FieldFormatterFactory.register(Object.class, new DefaultFieldFormatter());
}
@Override
public Set<Class> getValueTypes() {
Set<Class> 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 代码块没有执行
工厂,通过反射扫描指定的类
@Slf4j
public class FieldFormatterFactory {
private static Map<Class, FormatterHelper> formatterCacheMap = new HashMap<>(32);
static {
Reflections reflections = new Reflections(FieldFormatterFactory.class.getPackage().getName());
Set<Class<? extends FormatterHelper>> 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<Class> klsSet = formatterHelper.getValueTypes();
klsSet.forEach(valueType -> {
register(valueType, formatterHelper);
});
} catch (Exception e) {
log.error(e.getMessage(), e);
}
});
}
public static <T> FieldFormatter<T> get(@NonNull Class<T> 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()));
}
}
public class DefaultFieldFormatter extends AbstractFieldFormatter<Object> {
@Override
public Set<Class> getValueTypes() {
Set<Class> kls = new HashSet<>();
kls.add(Object.class);
return kls;
}
@Override
public String apply(ColMata colMata, Object o) {
return ObjectUtils.defaultIfNull(o, "").toString();
}
}