【问题标题】:Java Plugins With A Custom Interface具有自定义接口的 Java 插件
【发布时间】:2021-05-03 08:13:29
【问题描述】:

像 spigot/bukkit 插件 id 喜欢能够在文件中加载 jars 并加载它们的类。我已经设法让它与 java 类加载器一起工作,但该类必须扩展一个可运行的对象才能工作。我想为每个插件(jar)实现我自己的自定义接口。所以我可以拥有在插件加载时运行的函数等等。如果有人知道该怎么做,请告诉我。

【问题讨论】:

    标签: java class plugins jar loader


    【解决方案1】:

    插件结构

    插件是.jar 文件。该插件有一个包含插件属性的 plugin.properties 文件。

    看起来像这样:

    plugin.main=com.example.plugins.ExamplePlugin
    plugin.name=Example Plugin
    plugin.description=Test 123
    plugin.version=1.0
    

    该文件包含主类、插件名称、描述和版本。

    插件必须有一个继承自抽象插件类的类。这算作主类。

    代码结构

    让我们从Plugin-class 开始:

    package com.example.plugins;
    
    public abstract class Plugin {
        
        protected PluginProperty property;
        
        public abstract void onEnable();
        public abstract void onDisable();
    
        public PluginProperty getProperty() {
            return property;
        }
        public void setProperty(PluginProperty property) {
            this.property = property;
        }
    }
    

    如您所见,我在这里选择了一个抽象类。

    该类由两个抽象方法(onEnableonDisable)组成。该插件还有一个PluginProperty 对象。 Spigot 中这个类的等价物是JavaPlugin

    让我们看一下PluginProperty 类。

    package com.example.plugins;
    
    public class PluginProperty {
        
        private String main;
        
        private String name;
        private String description;
        private double version;
        
        public PluginProperty(String main, String name, String description, double version) {
            this.main = main;
            
            this.name = name;
            this.description = description;
            this.version = version;
        }
    
        public String getMain() {
            return main;
        }
    
        public void setMain(String main) {
            this.main = main;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getDescription() {
            return description;
        }
    
        public void setDescription(String description) {
            this.description = description;
        }
    
        public double getVersion() {
            return version;
        }
    
        public void setVersion(double version) {
            this.version = version;
        }
    }
    

    这个类包含插件的所有必要属性。这里的大部分内容是不言自明的,但我仍然想讨论main

    该字符串包含插件的主要插件类的名称。这与 Spigotplugin.yml 中的main 基本相同。

    加载器

    这里是PluginLoader-class:

    package com.example.plugins;
    
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    import java.lang.reflect.InvocationTargetException;
    import java.net.JarURLConnection;
    import java.net.URL;
    import java.net.URLClassLoader;
    import java.util.Properties;
    import java.util.zip.ZipException;
    
    public class PluginLoader {
        
        private static PluginProperty loadPluginProperties(File file) throws ZipException, IOException {
            URL url = file.toURI().toURL();
            String jarURL = "jar:" + url +"!/plugin.properties";
            
            InputStream input;
            URL inputURL = new URL(jarURL);
            JarURLConnection conn = (JarURLConnection)inputURL.openConnection();
            input = conn.getInputStream();
            
            Properties property = new Properties();
            
            property.load(input);
            
            String main = property.getProperty("plugin.main");
            
            String name = property.getProperty("plugin.name");
            String description = property.getProperty("description");
            double version = Double.parseDouble(property.getProperty("plugin.version"));
            
            
            return new PluginProperty(main, name, description, version);
        }
        
        public static Plugin loadPlugin(File file) throws IOException, InstantiationException, IllegalAccessException, ClassNotFoundException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
            if(!file.exists()) {
                return null;
            }
            
            PluginProperty property = loadPluginProperties(file);
            
            URL url = file.toURI().toURL();
            String jarURL = "jar:" + url + "!/";
            URL urls[] = {new URL(jarURL)};
            URLClassLoader ucl = new URLClassLoader(urls);
            Plugin plugin = (Plugin) Class.forName(property.getMain(), true, ucl).getDeclaredConstructor().newInstance();
            plugin.setProperty(property);
            
            return plugin;
        }
    }
    

    私有loadPluginProperties 方法加载插件属性并返回所需的对象。 loadPlugin 方法将属性中指定的主类加载到对象中并返回。

    示例

    我只是给了你插件系统的基本框架。但是你应该如何使用它呢?让我们从一个示例加载器开始。

    这里是Main-class:

    package com.example.plugins;
    
    import java.io.File;
    import java.io.IOException;
    import java.lang.reflect.InvocationTargetException;
    import java.util.ArrayList;
    import java.util.List;
    
    public class Main {
        
        public static List<Plugin> plugins = new ArrayList<Plugin>();
    
        public static void main(String[] args) {
            File[] pluginFiles = new File("plugins").listFiles();
            
            //Load plugins
            for(File f : pluginFiles) {
                if(f.isDirectory()) {
                    continue;
                }
                
                if(!f.getName().endsWith(".jar")) {
                    continue;
                }
                
                Plugin p = null;
                
                try {
                    p = PluginLoader.loadPlugin(f);
                } catch (InstantiationException | IllegalAccessException | ClassNotFoundException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException | IOException e) {
                    System.err.println("Failed to load plugin!");
                    
                    e.printStackTrace();
                }
                
                Main.plugins.add(p);
            }
            
            //Enable plugins
            for(Plugin p : plugins) {
                p.onEnable();
            }
            
            //Disable plugins
            for(Plugin p : plugins) {
                p.onDisable();
            }
        }
    
    }
    

    我不会在这里详细介绍,因为我认为它是不言自明的。如果您有任何问题,请通过 cmets 向我提问。


    将之前编写的JAR导出后,添加到新项目的classpath中。不要忘记创建一个plugin.properties 文件。

    这是一个与上面指定的.properties 文件兼容的示例插件:

    package com.example.plugins;
    
    public class ExamplePlugin extends Plugin {
    
        @Override
        public void onEnable() {
            System.out.println("Hello world!");
        }
        
        @Override
        public void onDisable() {
            
        }
    }
    

    当我导出这个插件并将它放在插件文件夹中时,我得到以下输出: Hello world!

    结束

    建议使用 JSON 或 YAML、XML 等代替内置的 Java 属性文件。这是插件的基本结构。玩得开心!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-31
      • 2019-07-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多