pf4j 插件加载机制
主要简单说明下pf4j 的插件加载处理
参考代码 pf4j/src/main/java/org/pf4j/AbstractPluginManager.java
加载代码
- loadPlugins 处理
加载处理
@Override
public void loadPlugins() {
log.debug("Lookup plugins in '{}'", pluginsRoots);
// check for plugins roots
if (pluginsRoots.isEmpty()) {
log.warn("No plugins roots configured");
return;
}
pluginsRoots.forEach(path -> {
if (Files.notExists(path) || !Files.isDirectory(path)) {
log.warn("No '{}' root", path);
}
});
// get all plugin paths from repository
// 查找路径
List<Path> pluginPaths = pluginRepository.getPluginPaths();
// check for no plugins
if (pluginPaths.isEmpty()) {
log.info("No plugins");
return;
}
log.debug("Found {} possible plugins: {}", pluginPaths.size(), pluginPaths);
// load plugins from plugin paths
for (Path pluginPath : pluginPaths) {
try {
// 加载发现到的插件,此方法比较重要
loadPluginFromPath(pluginPath);
} catch (PluginRuntimeException e) {
log.error(e.getMessage(), e);
}
}
// resolve plugins
try {
// 解析插件,主要是关于依赖,解析插件描述信息,然后进行插件状态的通知
resolvePlugins();
} catch (PluginRuntimeException e) {
log.error(e.getMessage(), e);
}
}
- loadPluginFromPath 方法
此方法比较重要,进行了插件的查找以及插件的classloader创建,形成一个插件的基本存储,方便后续流程使用
PluginWrapper 是一个比较重要的实体,包含了不少关于插件的元数据信息
protected PluginWrapper loadPluginFromPath(Path pluginPath) {
// Test for plugin path duplication
String pluginId = idForPath(pluginPath);
if (pluginId != null) {
throw new PluginAlreadyLoadedException(pluginId, pluginPath);
}
// Retrieve and validate the plugin descriptor
PluginDescriptorFinder pluginDescriptorFinder = getPluginDescriptorFinder();
log.debug("Use '{}' to find plugins descriptors", pluginDescriptorFinder);
log.debug("Finding plugin descriptor for plugin '{}'", pluginPath);
PluginDescriptor pluginDescriptor = pluginDescriptorFinder.find(pluginPath);
validatePluginDescriptor(pluginDescriptor);
// Check there are no loaded plugins with the retrieved id
pluginId = pluginDescriptor.getPluginId();
if (plugins.containsKey(pluginId)) {
PluginWrapper loadedPlugin = getPlugin(pluginId);
throw new PluginRuntimeException("There is an already loaded plugin ({}) "
+ "with the same id ({}) as the plugin at path '{}'. Simultaneous loading "
+ "of plugins with the same PluginId is not currently supported.\n"
+ "As a workaround you may include PluginVersion and PluginProvider "
+ "in PluginId.",
loadedPlugin, pluginId, pluginPath);
}
log.debug("Found descriptor {}", pluginDescriptor);
String pluginClassName = pluginDescriptor.getPluginClass();
log.debug("Class '{}' for plugin '{}'", pluginClassName, pluginPath);
// load plugin
log.debug("Loading plugin '{}'", pluginPath);
// 此处,保证每个插件会有一个不同的classloader,这个后续会介绍
ClassLoader pluginClassLoader = getPluginLoader().loadPlugin(pluginPath, pluginDescriptor);
log.debug("Loaded plugin '{}' with class loader '{}'", pluginPath, pluginClassLoader);
PluginWrapper pluginWrapper = createPluginWrapper(pluginDescriptor, pluginPath, pluginClassLoader);
// test for disabled plugin
if (isPluginDisabled(pluginDescriptor.getPluginId())) {
log.info("Plugin '{}' is disabled", pluginPath);
pluginWrapper.setPluginState(PluginState.DISABLED);
}
// validate the plugin
if (!isPluginValid(pluginWrapper)) {
log.warn("Plugin '{}' is invalid and it will be disabled", pluginPath);
pluginWrapper.setPluginState(PluginState.DISABLED);
}
log.debug("Created wrapper '{}' for plugin '{}'", pluginWrapper, pluginPath);
pluginId = pluginDescriptor.getPluginId();
// add plugin to the list with plugins
plugins.put(pluginId, pluginWrapper);
getUnresolvedPlugins().add(pluginWrapper);
// add plugin class loader to the list with class loaders
getPluginClassLoaders().put(pluginId, pluginClassLoader);
return pluginWrapper;
}
说明
以上是关于pf4j 插件加载的简单说明,实际上pf4j还包含插件的unload start stop。。。后续会介绍
参考资料
https://pf4j.org/doc/plugin-lifecycle.html
https://pf4j.org/doc/plugins.html