如何在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
相关产品推荐
相关产品推荐

