java插件化设计开发


问题

java实现动态从jar包加载插件并运行.

实现

  • 定义插件运行的接口,用户需要自己实现的

    public interface PluginService {
      /**
       * 插件运行方法
       */
      void process();
    }
  • 定义插件实体类

    @Data
    public class Plugin {
      /**
       * 插件名称
       */
      private String pluginName;
      /**
       * jar包完整路径
       */
      private String jarPath;
      /**
       * class完整路径
       */
      private String className;
    }
  • 定义插件异常

    public class PluginException extends Exception {
    
      public PluginException(String message) {
          super(message);
      }
    }
  • 定义插件管理类

    @Data
    @NoArgsConstructor
    public class PluginManager {
    
      private Map<String, Class> clazzMap = new HashMap<>();
    
      public PluginManager(List<Plugin> plugins) throws PluginException {
          initPlugins(plugins);
      }
    
      public void initPlugin(Plugin plugin) throws PluginException {
          try {
    //            URL url = new URL("file:" + plugin.getJarPath());
              URL url = new File(plugin.getJarPath()).toURI().toURL();
              URLClassLoader classLoader = new URLClassLoader(new URL[]{url});
              Class clazz = classLoader.loadClass(plugin.getClassName());
              clazzMap.put(plugin.getClassName(), clazz);
          } catch (Exception e) {
              throw new PluginException("plugin " + plugin.getPluginName() + " init error," + e.getMessage());
          }
      }
    
      public void initPlugins(List<Plugin> plugins) throws PluginException {
          for (Plugin plugin : plugins) {
              initPlugin(plugin);
          }
      }
    
      public PluginService getInstance(String className) throws PluginException {
          Class clazz = clazzMap.get(className);
          Object instance = null;
          try {
              instance = clazz.newInstance();
          } catch (Exception e) {
              throw new PluginException("plugin " + className + " instantiate error," + e.getMessage());
          }
          return (PluginService) instance;
      }
    }
  • 定义插件配置文件

    <!-- 采用xml方式存储配置,dom4j解析 -->
    <dependency>
      <groupId>org.dom4j</groupId>
      <artifactId>dom4j</artifactId>
      <version>2.1.1</version>
    </dependency>
public class PluginXmlParser {

    public static List<Plugin> getPluginList() throws PluginException {

        List<Plugin> list = new ArrayList<>();

        SAXReader saxReader = new SAXReader();
        Document document = null;
        try {
            document = saxReader.read(new File("plugin.xml"));
        } catch (Exception e) {
            throw new PluginException("read plugin.xml error," + e.getMessage());
        }
        Element root = document.getRootElement();
        List<?> plugins = root.elements("plugin");
        for (Object pluginObj : plugins) {
            Element pluginEle = (Element) pluginObj;
            Plugin plugin = new Plugin();
            plugin.setPluginName(pluginEle.elementText("name"));
            plugin.setJarPath(pluginEle.elementText("jar"));
            plugin.setClassName(pluginEle.elementText("class"));
            list.add(plugin);
        }
        return list;
    }
}
  • 运行测试类

    public class Main {
      public static void main(String[] args) throws PluginException {
          // 从配置文件加载插件
          List<Plugin> pluginList = PluginXmlParser.getPluginList();
          PluginManager pluginManager = new PluginManager(pluginList);
    
          for (Plugin plugin : pluginList) {
              PluginService pluginService = pluginManager.getInstance(plugin.getClassName());
              System.out.println("开始执行[" + plugin.getPluginName() + "]插件...");
              // 调用插件
              pluginService.process();
              System.out.println("[" + plugin.getPluginName() + "]插件执行完成");
          }
    
          // 动态加载插件
          Plugin plugin = new Plugin();
          plugin.setPluginName("B插件");
          plugin.setJarPath("D:\\flinkDemo\\java8\\out\\artifacts\\test\\test.jar");
          plugin.setClassName("com.deri.stream.plugina.PluginB");
          pluginManager.initPlugin(plugin);
          PluginService pluginService = pluginManager.getInstance("com.deri.stream.plugina.PluginB");
          pluginService.process();
      }
    }
  • 插件A/B/C

    public class PluginA implements PluginService {
      @Override
      public void process() {
          System.out.println("Plugin A.");
      }
    }
public class PluginB implements PluginService {
    @Override
    public void process() {
        System.out.println("Plugin B.");
    }
}
public class PluginC implements PluginService {
    @Override
    public void process() {
        System.out.println("Plugin C.");
    }
}
  • 插件配置文件
    <?xml version="1.0" encoding="UTF-8"?>
    <plugins>
      <plugin>
          <name>A插件</name>
          <jar>D:\flinkDemo\java8\out\artifacts\test\test.jar</jar>
          <class>com.deri.stream.plugina.PluginA</class>
      </plugin>
    </plugins>

参考链接


文章作者: wuzhiyong
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 wuzhiyong !
评论
 上一篇
springboot奇怪问题【1】—— HttpServletRequest和@Async搭配使用 springboot奇怪问题【1】—— HttpServletRequest和@Async搭配使用
HttpServletRequest和@Async搭配使用问题问题描述:希望将request中参数传递到service中处理,同时service是使用异步@Async,此时通过request.getParameterMap()获取参数列表只
2020-10-20
下一篇 
influxdb内存持续升高不主动释放问题 influxdb内存持续升高不主动释放问题
问题influxdb部署后,内存会随着查询等操作持续升高且不会主动释放. 分析 go在释放内存返回到内核时,在Linux上使用的是MADV_DONTNEED,虽然效率比较低,但是会让RSS(resident set size 常驻内存集)数
2020-09-21
  目录