前言
log4j2 使用插件机制加载各种组件:appender, logger .etc,本文简要分析 log4j2 插件机制实现
Plugin Annotation(注解)
Plugin 注解提供了一种便捷的方法将一个类声明成 log4j2 的插件,比如
@Plugin(name = "Console", category = "Core", elementType = "appender", printObject = true)
public final class ConsoleAppender extends
AbstractOutputStreamAppender {
...
}
name,name of the plugin
category, category to place the plugin under
name of the corresponding category of elements this plugin belongs under
Plugin Registry
单例
PluginRegistry 类用来保存插件信息,暴露了一些方法从配置文件中加载(内置)插件,使用了单例设计模式
private static volatile PluginRegistry INSTANCE;
private static final Object INSTANCE_LOCK = new Object();
private PluginRegistry() {
}
public static PluginRegistry getInstance() {
PluginRegistry result = INSTANCE;
if (result == null) {
synchronized(INSTANCE_LOCK) {
result = INSTANCE;
if (result == null) {
INSTANCE = result = new PluginRegistry();
}
}
}
}
线程安全的数据结构
PluginRegistry 使用了一些 Java 多线程编程的最佳实践:
使用 AtomicReference 类的 CAS(compare and set)操作,避免在多线程环境下 插件配置 被多次加载
使用 ConcurrentMapHash 类替代 HashMap 提供线程安全的 map
PluginType 类用来描述插件,比如插件对应的 class
/**
* Contains plugins found in Log4j2Plugins.dat cache files in the main CLASSPATH.
*/
private final AtomicReference
log4j2Plugins.dat 是插件描述文件,内部插件描述文件位于:
log4j-core-2.5.jar
META-INF
org.apache.logging.log4j.core.config.plugins
Log4j2Plugins.dat
加载插件
PluginRegistry 中和加载扫描插件相关的方法
loadFromMainClassLoader,加载内部插件
loadFromBundle,osgi相关
loadFromPackage,加载指定 package(包)中的插件
加载内部插件
public Map>> loadFromMainClassLoader() {
final Map>> existing = pluginsByCategoryRef.get();
// 如果 get 方法返回非空,说明已经通过 main class loader 加载过插件配置
if (existing != null) {
// already loaded
return existing;
}
// 从配置文件加载 插件配置
final Map>> newPluginsByCategory =
decodeCacheFiles(Loader.getClassLoader());
// CAS
if (pluginsByCategoryRef.compareAndSet(null, newPluginsByCategory)) {
return newPluginsByCategory;
}
return pluginsByCategoryRef.get();
}
加载指定包中插件
public Map>> loadFromPackage(final String pkg) {
// 参数校验
if (Strings.isBlank(pkg)) {
return Collections.emptyMap();
}
// 如果 pkg 已经被加载过直接返回
Map>> existing = pluginByCategoryByPackage.get(pkg);
if (existing != null) {
return existing;
}
// 加载 pkg
// 线程安全的 put if absent 操作(类似 CAS)
existing = pluginsByCategoryByPackage.putIfAbsent(pkg, newPluginsByCategory);
if (existing != null) {
return existing;
}
return newPluginsByCategory;
}
PluginManager
PluginManager 类用来加载和管理所有的插件,每一个 category(类别)都有一个对应的 PluginManager
public PluginManager(final String category) {
this.category = category;
}
collectPlugins 方法用于加载插件(描述信息)
public void collectPlugins(final List packages) {
...
}