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(比如我们我们包装一个基于网络的)

参考资料

https://pf4j.org/doc/class-loading.html

posted on 2022-02-16 21:37  荣锋亮  阅读(441)  评论(0编辑  收藏  举报

导航