Java Spigot插件开发问题:三个类无法协同工作引发命令执行异常
解决Spigot插件中的"Plugin already initialized!"错误
我正在开发一款Spigot插件,包含三个核心类:GUI、OpenGui和BanGUIMain,调用注册的/opengui命令时触发了Plugin already initialized!错误,相关代码和错误日志如下:
现有代码
GUI类
public class GUI { private BanGUIMain main = new BanGUIMain(); /*line 16*/ public void createOptionsGUI(Player p) {} }
OpenGui类
public class OpenGui implements CommandExecutor { @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { if (sender instanceof Player) { if (sender.hasPermission("bangui.opengui")) { GUI gui = new GUI(); /*line 14*/ gui.createOptionsGUI((Player) sender); } } return false; } }
BanGUIMain类
public final class BanGUIMain extends JavaPlugin { /*this is line 12*/ // 无关代码省略 }
触发的错误日志
[16:29:55 ERROR]: null org.bukkit.command.CommandException: Unhandled exception executing command 'opengui' in plugin BanGUIMain v1.0.0 at org.bukkit.command.PluginCommand.execute(PluginCommand.java:46) ~[server.jar:git-Spigot-db6de12-18fbb24] at org.bukkit.command.SimpleCommandMap.dispatch(SimpleCommandMap.java:141) ~[server.jar:git-Spigot-db6de12-18fbb24] at org.bukkit.craftbukkit.v1_8_R3.CraftServer.dispatchCommand(CraftServer.java:641) ~[server.jar:git-Spigot-db6de12-18fbb24] at net.minecraft.server.v1_8_R3.PlayerConnection.handleCommand(PlayerConnection.java:1162) [server.jar:git-Spigot-db6de12-18fbb24] at net.minecraft.server.v1_8_R3.PlayerConnection.a(PlayerConnection.java:997) [server.jar:git-Spigot-db6de12-18fbb24] at net.minecraft.server.v1_8_R3.PacketPlayInChat.a(PacketPlayInChat.java:45) [server.jar:git-Spigot-db6de12-18fbb24] at net.minecraft.server.v1_8_R3.PacketPlayInChat.a(PacketPlayInChat.java:1) [server.jar:git-Spigot-db6de12-18fbb24] at net.minecraft.server.v1_8_R3.PlayerConnectionUtils$1.run(SourceFile:13) [server.jar:git-Spigot-db6de12-18fbb24] at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) [?:1.8.0_271] at java.util.concurrent.FutureTask.run(Unknown Source) [?:1.8.0_271] at net.minecraft.server.v1_8_R3.SystemUtils.a(SourceFile:44) [server.jar:git-Spigot-db6de12-18fbb24] at net.minecraft.server.v1_8_R3.MinecraftServer.B(MinecraftServer.java:715) [server.jar:git-Spigot-db6de12-18fbb24] at net.minecraft.server.v1_8_R3.DedicatedServer.B(DedicatedServer.java:374) [server.jar:git-Spigot-db6de12-18fbb24] at net.minecraft.server.v1_8_R3.MinecraftServer.A(MinecraftServer.java:654) [server.jar:git-Spigot-db6de12-18fbb24] at net.minecraft.server.v1_8_R3.MinecraftServer.run(MinecraftServer.java:557) [server.jar:git-Spigot-db6de12-18fbb24] at java.lang.Thread.run(Unknown Source) [?:1.8.0_271] Caused by: java.lang.IllegalArgumentException: Plugin already initialized! at org.bukkit.plugin.java.PluginClassLoader.initialize(PluginClassLoader.java:122) ~[server.jar:git-Spigot-db6de12-18fbb24] at org.bukkit.plugin.java.JavaPlugin.<init>(JavaPlugin.java:66) ~[server.jar:git-Spigot-db6de12-18fbb24] at me.roughlyunderscore.plugins.premium.bangui.main.BanGUIMain.<init>(BanGUIMain.java:12) ~[?:?] at me.roughlyunderscore.plugins.premium.bangui.main.gui.GUI.<init>(GUI.java:16) ~[?:?] at me.roughlyunderscore.plugins.premium.bangui.main.commands.OpenGui.onCommand(OpenGui.java:14) ~[?:?] at org.bukkit.command.PluginCommand.execute(PluginCommand.java:44) ~[server.jar:git-Spigot-db6de12-18fbb24] ... 15 more Caused by: java.lang.IllegalStateException: Initial initialization at org.bukkit.plugin.java.PluginClassLoader.initialize(PluginClassLoader.java:125) ~[server.jar:git-Spigot-db6de12-18fbb24] at org.bukkit.plugin.java.JavaPlugin.<init>(JavaPlugin.java:66) ~[server.jar:git-Spigot-db6de12-18fbb24] at me.roughlyunderscore.plugins.premium.bangui.main.BanGUIMain.<init>(BanGUIMain.java:12) ~[?:?] at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[?:1.8.0_271] at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source) ~[?:1.8.0_271] at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source) ~[?:1.8.0_271] at java.lang.reflect.Constructor.newInstance(Unknown Source) ~[?:1.8.0_271] at java.lang.Class.newInstance(Unknown Source) ~[?:1.8.0_271] at org.bukkit.plugin.java.PluginClassLoader.<init>(PluginClassLoader.java:76) ~[server.jar:git-Spigot-db6de12-18fbb24] at org.bukkit.plugin.java.JavaPluginLoader.loadPlugin(JavaPluginLoader.java:131) ~[server.jar:git-Spigot-db6de12-18fbb24] at org.bukkit.plugin.SimplePluginManager.loadPlugin(SimplePluginManager.java:329) ~[server.jar:git-Spigot-db6de12-18fbb24] at org.bukkit.plugin.SimplePluginManager.loadPlugins(SimplePluginManager.java:251) ~[server.jar:git-Spigot-db6de12-18fbb24] at org.bukkit.craftbukkit.v1_8_R3.CraftServer.loadPlugins(CraftServer.java:292) ~[server.jar:git-Spigot-db6de12-18fbb24] at net.minecraft.server.v1_8_R3.DedicatedServer.init(DedicatedServer.java:198) ~[server.jar:git-Spigot-db6de12-18fbb24] at net.minecraft.server.v1_8_R3.MinecraftServer.run(MinecraftServer.java:525) ~[server.jar:git-Spigot-db6de12-18fbb24] ... 1 more
错误原因分析
你的推测方向没错,但核心问题不是循环实例化或者需要销毁对象,而是Spigot插件的主类(继承JavaPlugin的类)是单例模式——只能由Spigot服务器在加载插件时初始化一次。
当你在GUI类里手动new BanGUIMain()时,相当于尝试创建第二个插件实例,而Spigot绝对不允许一个插件同时存在多个实例,所以直接抛出了Plugin already initialized!异常。
简单说:你永远不要自己去new主类对象,主类的实例是服务器帮你创建好的,其他类只需要用这个现成的实例就行。
解决方法:依赖注入(传递主类实例)
我们需要把服务器已经创建好的BanGUIMain实例传递给OpenGui和GUI类,而不是让它们自己去新建主类对象。具体步骤如下:
1. 修改主类BanGUIMain,传递自身实例给命令类
在主类的onEnable()方法中注册命令时,把主类的this(当前实例)传给OpenGui:
public final class BanGUIMain extends JavaPlugin { @Override public void onEnable() { // 注册/opengui命令,将主类实例传递给OpenGui this.getCommand("opengui").setExecutor(new OpenGui(this)); } }
2. 修改OpenGui类,接收主类实例并传递给GUI
让OpenGui持有主类实例,创建GUI对象时把这个实例传进去:
public class OpenGui implements CommandExecutor { private final BanGUIMain main; // 构造方法接收主类实例 public OpenGui(BanGUIMain main) { this.main = main; } @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { if (sender instanceof Player) { Player player = (Player) sender; if (player.hasPermission("bangui.opengui")) { // 创建GUI时传递主类实例 GUI gui = new GUI(main); gui.createOptionsGUI(player); } } return true; // 建议返回true表示命令执行成功(可根据需求调整) } }
3. 修改GUI类,接收主类实例
去掉new BanGUIMain()的代码,改为通过构造方法接收主类实例:
public class GUI { private final BanGUIMain main; // 构造方法接收主类实例 public GUI(BanGUIMain main) { this.main = main; } public void createOptionsGUI(Player p) { // 在这里可以正常使用main实例,比如调用main.getConfig()获取配置、main.getServer()获取服务器对象等 } }
额外说明
- 不需要手动销毁
GUI实例:Java的垃圾回收机制会自动处理不再被引用的对象,你完全不用手动管理销毁逻辑。 - 用
final修饰主类实例是好习惯:可以确保这个实例不会被意外修改,避免潜在的bug。 - 这种传递实例的方式叫依赖注入,是Spigot插件开发的标准做法,后续你的监听器、其他命令类如果需要用主类实例,都可以用同样的方式传递。
内容的提问来源于stack exchange,提问作者LeopardL GD
相关产品推荐
相关产品推荐

