pf4j 类加载顺序简单说明
pf4j 具有类隔离特性
参考代码
pf4j/src/main/java/org/pf4j/ClassLoadingStrategy.java
pf4j/src/main/java/org/pf4j/PluginClassLoader.java
pf4j/src/main/java/org/pf4j/DefaultPluginManager.java
加载顺序
- 顺序说明(注意顺序)
- 如果类是系统类,使用系统类加载器
- 如果是pf4j引擎的,使用应用加载器
- 尝试加载插件的类
- 插件的没有,委托到依赖的类加载器
- 然后在委托到父加载器
参考原理
- 调用类加载器的方法,在PluginManager 中定义了createPluginLoader,每个PluginLoader 定义了自己的classloader顺序
我们可以自己编写自己的在扩展
@Override
protected PluginLoader createPluginLoader() {
return new CompoundPluginLoader()
.add(new DevelopmentPluginLoader(this), this::isDevelopment)
.add(new JarPluginLoader(this), this::isNotDevelopment)
.add(new DefaultPluginLoader(this), this::isNotDevelopment);
}
- pluginloader 参考定义
public interface PluginLoader {
/**
* Returns true if this loader is applicable to the given {@link Path}.
*
* @param pluginPath
* @return
*/
boolean isApplicable(Path pluginPath);
ClassLoader loadPlugin(Path pluginPath, PluginDescriptor pluginDescriptor);
}
jar 以及DefaultPluginLoader的实现处理都使用了
pf4j/src/main/java/org/pf4j/PluginClassLoader.java
public PluginClassLoader(PluginManager pluginManager, PluginDescriptor pluginDescriptor, ClassLoader parent) {
this(pluginManager, pluginDescriptor, parent, ClassLoadingStrategy.PDA);
}
顺序如下:
pf4j/src/main/java/org/pf4j/ClassLoadingStrategy.java
/**
* plugin -> dependencies -> application(parent)
*/
public static final ClassLoadingStrategy PDA = new ClassLoadingStrategy(Arrays.asList(Source.PLUGIN, Source.DEPENDENCIES, Source.APPLICATION));
当然系统已经定义了不少其他的可以使用没具体可以参考ClassLoadingStrategy
- PluginClassLoader 处理
参考代码
pf4j/src/main/java/org/pf4j/PluginClassLoader.java
以下逻辑也印证了官方文档的说法
@Override
public Class<?> loadClass(String className) throws ClassNotFoundException {
synchronized (getClassLoadingLock(className)) {
// first check whether it's a system class, delegate to the system loader
if (className.startsWith(JAVA_PACKAGE_PREFIX)) {
return findSystemClass(className);
}
// if the class is part of the plugin engine use parent class loader
if (className.startsWith(PLUGIN_PACKAGE_PREFIX) && !className.startsWith("org.pf4j.demo") && !className.startsWith("org.pf4j.test")) {
// log.trace("Delegate the loading of PF4J class '{}' to parent", className);
return getParent().loadClass(className);
}
log.trace("Received request to load class '{}'", className);
// second check whether it's already been loaded
Class<?> loadedClass = findLoadedClass(className);
if (loadedClass != null) {
log.trace("Found loaded class '{}'", className);
return loadedClass;
}
for (ClassLoadingStrategy.Source classLoadingSource : classLoadingStrategy.getSources()) {
Class<?> c = null;
try {
switch (classLoadingSource) {
case APPLICATION:
c = super.loadClass(className);
break;
case PLUGIN:
c = findClass(className);
break;
case DEPENDENCIES:
c = loadClassFromDependencies(className);
break;
}
} catch (ClassNotFoundException ignored) {}
if (c != null) {
log.trace("Found class '{}' in {} classpath", className, classLoadingSource);
return c;
} else {
log.trace("Couldn't find class '{}' in {} classpath", className, classLoadingSource);
}
}
throw new ClassNotFoundException(className);
}
}
但是有一个问题就是关于自定义PluginClassLoader的问题,官方的文档已经不对了
我们如果需要对与特定的pluginloader 进行自定义classlaoder ,可以通过重写createPluginLoader以及如下几个方法
(PluginManager 子类)
protected Map<String, ClassLoader> getPluginClassLoaders() {}
以及
public ClassLoader getPluginClassLoader(String pluginId) {}
当时从目前pf4j 框架来说,改写getPluginClassLoaders 以及getPluginClassLoader 对于系统的机制影响会比较大,推荐还是自己自定义的pluginloader
加载自己定义的classloader(比如我们我们包装一个基于网络的)