You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Minecraft插件中自动执行包内所有类的构造方法

解决Minecraft插件中自动执行包内类构造方法的问题

问题核心

需要实现一个类,自动执行所在包内除自身外所有类的构造方法,后续向包中新增类时,无需修改主类代码,构造方法就能自动触发。当前通过类加载器读取文件的方式在普通项目中可行,但打包成Jar运行的Minecraft插件中失效。

原代码失效原因

原代码仅处理了file协议的资源路径,而Minecraft插件是以Jar包形式运行的,资源协议为jar,因此无法读取到包内的类文件。

修正后的解决方案

1. 同时支持File和Jar协议的类扫描方法

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class Loader {
    public static List<Class<?>> getClassList() {
        List<Class<?>> classList = new ArrayList<>();
        String packageName = Loader.class.getPackage().getName();
        String packagePath = packageName.replace('.', '/');

        try {
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            if (classLoader == null) {
                throw new ClassNotFoundException("无法获取类加载器");
            }

            Enumeration<URL> resources = classLoader.getResources(packagePath);
            while (resources.hasMoreElements()) {
                URL resource = resources.nextElement();
                String protocol = resource.getProtocol();
                if ("file".equals(protocol)) {
                    getClassObjectsFromFile(packageName, resource.getPath(), classList);
                } else if ("jar".equals(protocol)) {
                    getClassObjectsFromJar(packageName, resource, classList);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 移除自身类
        classList.remove(Loader.class);
        return classList;
    }

    private static void getClassObjectsFromFile(String packageName, String filePath, List<Class<?>> classList) throws ClassNotFoundException {
        File directory = new File(filePath);
        if (directory.exists()) {
            File[] files = directory.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.isFile() && file.getName().endsWith(".class")) {
                        String className = packageName + '.' + file.getName().substring(0, file.getName().length() - 6);
                        classList.add(Class.forName(className));
                    }
                }
            }
        }
    }

    private static void getClassObjectsFromJar(String packageName, URL resource, List<Class<?>> classList) throws IOException, ClassNotFoundException {
        // 解析Jar URL格式:jar:file:/path/to/jar!/package/path
        String jarPath = resource.getPath().substring(5, resource.getPath().indexOf('!'));
        JarFile jarFile = new JarFile(URLDecoder.decode(jarPath, StandardCharsets.UTF_8.name()));
        Enumeration<JarEntry> entries = jarFile.entries();

        while (entries.hasMoreElements()) {
            JarEntry entry = entries.nextElement();
            String entryName = entry.getName();
            // 过滤当前包下的.class文件,排除目录
            if (entryName.startsWith(packagePath) && entryName.endsWith(".class") && !entry.isDirectory()) {
                // 转换为类全限定名
                String className = entryName.substring(0, entryName.length() - 6).replace('/', '.');
                classList.add(Class.forName(className));
            }
        }
        jarFile.close();
    }
}

2. 自动执行构造方法的调用逻辑

public static void loadAllClasses() {
    List<Class<?>> classList = getClassList();
    for (Class<?> clazz : classList) {
        try {
            // 调用无参构造方法,若构造方法私有需先设置可访问
            clazz.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            System.err.println("实例化类 " + clazz.getName() + " 失败: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

3. 插件中集成方式

在Minecraft插件的主类onEnable()方法中调用Loader.loadAllClasses()即可启动自动加载:

@Override
public void onEnable() {
    Loader.loadAllClasses();
    // 其他插件初始化逻辑
}

关键注意事项

  • 包内的类必须包含无参构造方法,否则会触发实例化异常;若构造方法为私有,需先调用clazz.getDeclaredConstructor().setAccessible(true)开启访问权限。
  • 扫描逻辑会自动排除Loader自身类,避免循环实例化。

内容的提问来源于stack exchange,提问作者Michael Capelli

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 19:15:22