zoukankan      html  css  js  c++  java
  • Pf4j的SPI简单实例

      同Dubbo的扩展SPI一样,Pf4j这个插件框架也师出同门,都是由JDK自带的SPI(参见Java的SPI简单实例)衍化而来。但Pf4j毕竟是一个插件框架,对插件的支持相对专业一些。官网上的介绍说:PF4J是一个开源(Apache许可证)轻量级(约100kb)的java插件框架,具有最小的依赖性(只有slf4japi和javasemver),并且具有很强的可扩展性。接下来我们还是用一个简单的例子说明:

      1、接口类,继承ExtensionPoint:

    package com.wlf.service;
    
    import org.pf4j.ExtensionPoint;
    
    public interface IPf4jGreeting extends ExtensionPoint {
    
        void sayHello();
    }

      2、接口实现类,需要@Extension注解证明它是扩展点的实现。一般都放在jar包或zip包中,但这里我们为了方便,直接放在同一个项目中:

    package com.wlf.service.impl;
    
    import com.wlf.service.IPf4jGreeting;
    import org.pf4j.Extension;
    
    @Extension
    public class IPf4jGreetingImpl1 implements IPf4jGreeting {
        @Override
        public void sayHello() {
            System.out.println("hello, world.");
        }
    }
    package com.wlf.service.impl;
    
    import com.wlf.service.IPf4jGreeting;
    import org.pf4j.Extension;
    
    @Extension
    public class IPf4jGreetingImpl2 implements IPf4jGreeting {
        @Override
        public void sayHello() {
            System.out.println("hi, mia.");
        }
    }

      3、应用类,执行插件加载和实例化:

    package com.wlf.service;
    
    import org.pf4j.DefaultPluginManager;
    import org.pf4j.PluginManager;
    
    import java.util.List;
    
    public class TestPf4jServiceLoader {
        public static void main(String[] args) {
            PluginManager pluginManager = new DefaultPluginManager();
    
            List<IPf4jGreeting> greetings = pluginManager.getExtensions(IPf4jGreeting.class);
            greetings.forEach(greeting -> {
                greeting.sayHello();
            });
    
            pluginManager.stopPlugins();
        }
    }

      完事了。跑一下试试:

    23:03:44.619 [main] INFO org.pf4j.DefaultPluginStatusProvider - Enabled plugins: []
    23:03:44.627 [main] INFO org.pf4j.DefaultPluginStatusProvider - Disabled plugins: []
    23:03:44.637 [main] INFO org.pf4j.DefaultPluginManager - PF4J version 3.0.1 in 'deployment' mode
    23:03:44.637 [main] DEBUG org.pf4j.AbstractPluginManager - Lookup plugins in 'plugins'
    23:03:44.641 [main] WARN org.pf4j.AbstractPluginManager - No 'plugins' root
    23:03:44.650 [main] DEBUG org.pf4j.AbstractExtensionFinder - Finding extensions of extension point 'com.wlf.service.IPf4jGreeting'
    23:03:44.650 [main] DEBUG org.pf4j.LegacyExtensionFinder - Reading extensions storages from classpath
    23:03:44.650 [main] DEBUG org.pf4j.LegacyExtensionFinder - Read '/E:/workspace/subtitle-synthesis/target/classes/META-INF/extensions.idx'
    23:03:44.664 [main] DEBUG org.pf4j.AbstractExtensionFinder - Found possible 2 extensions:
    23:03:44.665 [main] DEBUG org.pf4j.AbstractExtensionFinder -    com.wlf.service.impl.IPf4jGreetingImpl2
    23:03:44.665 [main] DEBUG org.pf4j.AbstractExtensionFinder -    com.wlf.service.impl.IPf4jGreetingImpl1
    23:03:44.665 [main] DEBUG org.pf4j.LegacyExtensionFinder - Reading extensions storages from plugins
    23:03:44.665 [main] DEBUG org.pf4j.AbstractExtensionFinder - Finding extensions of extension point 'com.wlf.service.IPf4jGreeting' for plugin 'null'
    23:03:44.665 [main] DEBUG org.pf4j.AbstractExtensionFinder - Loading class 'com.wlf.service.impl.IPf4jGreetingImpl2' using class loader 'sun.misc.Launcher$AppClassLoader@18b4aac2'
    23:03:44.673 [main] DEBUG org.pf4j.AbstractExtensionFinder - Checking extension type 'com.wlf.service.impl.IPf4jGreetingImpl2'
    23:03:44.697 [main] DEBUG org.pf4j.AbstractExtensionFinder - Added extension 'com.wlf.service.impl.IPf4jGreetingImpl2' with ordinal 0
    23:03:44.697 [main] DEBUG org.pf4j.AbstractExtensionFinder - Loading class 'com.wlf.service.impl.IPf4jGreetingImpl1' using class loader 'sun.misc.Launcher$AppClassLoader@18b4aac2'
    23:03:44.708 [main] DEBUG org.pf4j.AbstractExtensionFinder - Checking extension type 'com.wlf.service.impl.IPf4jGreetingImpl1'
    23:03:44.709 [main] DEBUG org.pf4j.AbstractExtensionFinder - Added extension 'com.wlf.service.impl.IPf4jGreetingImpl1' with ordinal 0
    23:03:44.710 [main] DEBUG org.pf4j.AbstractExtensionFinder - Found 2 extensions for extension point 'com.wlf.service.IPf4jGreeting'
    23:03:44.710 [main] DEBUG org.pf4j.AbstractExtensionFinder - Found 2 extensions for extension point 'com.wlf.service.IPf4jGreeting'
    23:03:44.710 [main] DEBUG org.pf4j.DefaultExtensionFactory - Create instance for extension 'com.wlf.service.impl.IPf4jGreetingImpl2'
    23:03:44.711 [main] DEBUG org.pf4j.DefaultExtensionFactory - Create instance for extension 'com.wlf.service.impl.IPf4jGreetingImpl1'
    hi, mia.
    hello, world.

       按通常用法,需要把实现类1和实现类2分别打成两个jar包或zip包,然后放到一个指定目录(默认是一个叫plugins的目录)中。这里为啥放在同一个项目中,Pf4j还能找到我们的扩展点实现类呢?其实日志已经出卖了一切,它最开始就是去plugins目录中寻找的,没找着,接着去classpath目录下需要,在target目录中找到了编译后extension.idx文件:

      文件的内容刚好就是@Extension注解标识出来的扩展点实现类。Pf4j通过getExtensions方法找到我们的实现类后,做了一层包装,把实现类包装进ExtensionWrapper对象,再由该对象的getExtension方法,调用扩展工厂ExtensionFactory生成实例:

    /**
     * A wrapper over extension instance.
     *
     * @author Decebal Suiu
     */
    public class ExtensionWrapper<T> implements Comparable<ExtensionWrapper<T>> {
    
        private final ExtensionDescriptor descriptor;
        private final ExtensionFactory extensionFactory;
        private T extension; // cache
    
        public ExtensionWrapper(ExtensionDescriptor descriptor, ExtensionFactory extensionFactory) {
            this.descriptor = descriptor;
            this.extensionFactory = extensionFactory;
        }
    
        @SuppressWarnings("unchecked")
        public T getExtension() {
            if (extension == null) {
                extension = (T) extensionFactory.create(descriptor.extensionClass);
            }
    
            return extension;
        }
    
        public ExtensionDescriptor getDescriptor() {
            return descriptor;
        }
    
        public int getOrdinal() {
            return descriptor.ordinal;
        }
    
        @Override
        public int compareTo(ExtensionWrapper<T> o) {
            return (getOrdinal() - o.getOrdinal());
        }
    
    }
    **
     * The default implementation for {@link ExtensionFactory}.
     * It uses {@link Class#newInstance} method.
     *
     * @author Decebal Suiu
     */
    public class DefaultExtensionFactory implements ExtensionFactory {
    
        private static final Logger log = LoggerFactory.getLogger(DefaultExtensionFactory.class);
    
        /**
         * Creates an extension instance.
         */
        @Override
        public <T> T create(Class<T> extensionClass) {
            log.debug("Create instance for extension '{}'", extensionClass.getName());
            try {
                return extensionClass.newInstance();
            } catch (Exception e) {
                throw new PluginRuntimeException(e);
            }
        }
    
    }

      包装类ExtensionWrapper做了排序,所以我们可以指定插件加载的顺序,ordinal默认是0,所以我们只需指定插件2的排序为1,即可让插件1先执行:

      既然Pf4j是基于JDK的SPI机制而构建的,自然我们也可以直接使用ServiceLoader指定的META-INF/services目录来让Pf4j发现我们的扩展点实现类,此时就没有必要使用注解@Extension来标识了。当然,没有了注解,我们也没法指定扩展点实现类的加载顺序,原生SPI没有那么强的功能。

      我们去掉两个实现类的注解:

       在META-INF/services目录新增文件com.wlf.service.IPf4jGreeting:

       最后在实例化PluginManager时指定使用JDK的SPI方式加载,重写了createExtensionFinder方法,运行结果如下:

       它会读取所有META-INF/services目录下的文件,包括依赖的jar包。所以为了排除一些类加载失败,我把其他jar依赖去掉了,日志也就打不出来了。

      

  • 相关阅读:
    df值自由度学习[转载]
    调用sklearn包中的PLA算法[转载]
    箱线图+小提琴图学习[转载]
    P1616 疯狂的采药 洛谷
    P1164 小A点菜 洛谷
    【noip模拟赛3】确定的位置 (map的遍历 位置原理)
    【noip模拟赛1】古韵之鹊桥相会(最短路)
    【noip模拟赛3】贾老二的工件 (模拟)
    【noip模拟赛1】古韵之乞巧 (dp)
    【noip模拟赛3】拣钱
  • 原文地址:https://www.cnblogs.com/wuxun1997/p/13290741.html
Copyright © 2011-2022 走看看