本帖最后由 M8_mve 于 2020-2-7 00:23 编辑
 
 
 
 
 用特别的方式加载插件 
 
 
 看这篇水贴教程之前需要 
 Java基础 
 反射知识 
 类加载器的简单原理 
 对ClassLoader的理解 
 
 
 
 
 
 
 这篇教程是哪里来的 
 之前写过一个UI化服务器的插件烂坑 
 是用Java FX写的UI 
 但是插件每次reload之后会重新实例化Plugin 
 这就会重新创建FX Application然后丢异常给我 
 我想能不能让插件只会实例化一次呢? 
 然后就有了这个想法和这篇教程 
 
 
 
 创建一个类做下面加载的类的父类,当然也可以直接用子类 - public class HyperPlugin
  - {
  -         public Plugin plugin;
  
 -         public void onLoad() { }
  
 -         public void onEnable() { }
  
 -         public void onDisable() { }
  - }
 
  复制代码这里的Plugin是保存每次加载时候的Plugin实例的,因为每次加载插件插件实例都会改变 因为plugin实例要在调用onEnable之前更新,访问权限不公开的话要用反射改,会拉低效率 当然如果你不考虑效率也可以用反射 反正都能反射改,公开不公开又有什么区别呢233
 一会要写的Core的主类要继承这个类,重写这里的三个方法 当然也可以改个名字,或者加一些别的方法  |  
  |  
 创建一个用来加载Core的类加载器,可以继承java的URLClassLoader但是要打破双亲委派的规则 - public class HyperPluginClassLoader extends URLClassLoader
  - {
  -         public static final HyperPluginClassLoader LOADER;
  -         private final Map<String, Class<?>> classes = new ConcurrentHashMap<>();
  -         private final JarFile jar;
  -         private final HyperPlugin plugin;
  -         private Map<String, Class<?>> parentClasses = new HashMap<>();
  
 -         private HyperPluginClassLoader(File pluginFile) throws IOException
  -         {
  -                 super(new URL[]{pluginFile.toURI().toURL()});
  -                 this.jar = new JarFile(pluginFile);
  -                 HyperPlugin plugin = null;
  -                 try
  -                 {
  -                         Class<?> main = Class.forName("org.mve.hy.Main", true, this);
  -                         plugin = (HyperPlugin) main.newInstance();
  -                 }
  -                 catch (Exception e)
  -                 {
  -                         e.printStackTrace();
  -                 }
  -                 this.plugin = plugin;
  -                 if (this.plugin == null)
  -                         throw new IllegalStateException("Can not load plugin: "+pluginFile);
  
 -                 this.plugin.onLoad();
  -         }
  
 -         public void onEnable(Plugin plugin)
  -         {
  -                 this.parentClasses.putAll(this.classes);
  -                 this.plugin.plugin = plugin;
  -                 this.plugin.onEnable();
  -         }
  
 -         public void onDisable()
  -         {
  -                 this.plugin.onDisable();
  -                 this.plugin.plugin = null;
  -         }
  
 -         @Override
  -         public Class<?> findClass(String name) throws ClassNotFoundException
  -         {
  -                 return this.loadClass(name);
  -         }
  
 -         @Override
  -         public Class<?> loadClass(String name) throws ClassNotFoundException
  -         {
  -                 Class<?> clazz = this.classes.get(name);
  -                 if (clazz != null) return clazz;
  -                 clazz = this.parentClasses.get(name);
  -                 if (clazz != null) return clazz;
  -                 try
  -                 {
  -                         String path = name.replace('.', '/').concat(".class");
  -                         JarEntry entry = jar.getJarEntry(path);
  -                         if (entry != null)
  -                         {
  -                                 InputStream in = this.jar.getInputStream(entry);
  -                                 byte[] bytes = HyperPluginClassLoader.toByteArray(in);
  -                                 in.close();
  -                                 clazz = this.defineClass(name, bytes, 0, bytes.length);
  -                                 if (clazz != null)
  -                                 {
  -                                         this.classes.put(name, clazz);
  -                                         this.parentClasses.put(name, clazz);
  -                                 }
  -                         }
  -                 }
  -                 catch (Exception ignored) {}
  -                 return clazz != null ? clazz : this.getParent().loadClass(name);
  -         }
  
 -         private static byte[] toByteArray(InputStream in) throws IOException
  -         {
  -                 ByteArrayOutputStream out = new ByteArrayOutputStream();
  -                 int len;
  -                 byte[] b = new byte[1024];
  -                 while ((len = in.read(b)) > -1)
  -                 {
  -                         out.write(b, 0, len);
  -                 }
  -                 out.flush();
  -                 out.close();
  -                 return out.toByteArray();
  -         }
  
 -         static
  -         {
  -                 HyperPluginClassLoader loader = null;
  -                 try
  -                 {
  -                         File coreFile = new File("plugins/HyperPlugin/Core.jar");
  -                         loader = new HyperPluginClassLoader(coreFile);
  -                 }
  -                 catch (IOException e)
  -                 {
  -                         e.printStackTrace();
  -                 }
  -                 LOADER = loader;
  -         }
  - }
 
  复制代码 这里的loadClass和findClass要自己实现,我把findClass推给了loadClass
 我这里用了单例模式,防止反复实例化classloader loadClass一定不能遵循双亲委派的规则,不然会出现ClassNotFoundException之类的奇怪的东西 这里的parentClasses是加载插件的PluginClassLoader的classes 这样可以假装自己加载过的类,父类加载器也加载过 如果其他插件要连接你的插件需要这个东西 在loadClass方法里加载新的类需要把加载的Class实例也put到这个parentClasses里 但是自己保存的classes也是必须的,因为每次重载插件,父类加载器都会变 这样这里的parentClasses也要变,在每次onEnable的时候需要把自己加载过的所有类都put进去 注意: 这里的静态代码块里指定的File一定是之后插件本体的位置 当然你可以改路径和名字但是别忘了改这里 这里的构造方法里加载的类一定是插件本体继承之前写的HyperPlugin的类  |  
  |  
  |  
 
 
 现在写的类是让Bukkit来加载的 - public class PluginMain extends JavaPlugin
  - {
  -         @Override
  -         public void onEnable()
  -         {
  -                 InputStream pluginIn = this.getResource("HyperPlugin.class");
  -                 InputStream loaderIn = this.getResource("HyperPluginClassLoader.class");
  -                 if (pluginIn == null)
  -                         throw new IllegalStateException(
  -                                 "Can not load core",
  -                                 new FileNotFoundException("HyperPlugin.class")
  -                         );
  -                 if (loaderIn == null)
  -                         throw new IllegalStateException(
  -                                 "Can not load core",
  -                                 new FileNotFoundException("HyperPluginClassLoader.class")
  -                         );
  -                 try
  -                 {
  -                         SystemClassLoader.define(IO.toByteArray(pluginIn));
  -                         SystemClassLoader.define(IO.toByteArray(loaderIn));
  -                 }
  -                 catch (Throwable e)
  -                 {
  -                         if (! (e instanceof LinkageError))
  -                                 throw new IllegalStateException("Can not load core", e);
  -                 }
  -                 try
  -                 {
  -                         Reflect.setFinalField(
  -                                 ClassLoader.class,
  -                                 "parent",
  -                                 HyperPluginClassLoader.LOADER,
  -                                 this.getClass().getClassLoader()
  -                         );
  -                         Reflect.setField(
  -                                 HyperPluginClassLoader.class,
  -                                 "parentClasses",
  -                                 HyperPluginClassLoader.LOADER,
  -                                 Reflect.reflectField(
  -                                         PluginClassLoader.class,
  -                                         "classes"
  -                                 )
  -                         );
  -                 }
  -                 catch (NoSuchFieldException | IllegalAccessException e)
  -                 {
  -                         e.printStackTrace();
  -                 }
  -                 HyperPluginClassLoader.LOADER.onEnable(this);
  -         }
  
 -         @Override
  -         public void onDisable()
  -         {
  -                 HyperPluginClassLoader.LOADER.onDisable();
  -         }
  - }
 
  复制代码 |  
  |  
 之前写的HyperPlugin和HyperPluginClassLoader这两个类一定不能直接丢到插件里 不能让PluginClassLoader加载它,不然和普通的插件加载没什么区别了 只要这两个class不在package指定的包里就可以,我直接放在缺省包里了 SystemClassLoader.define(IO.toByteArray(pluginIn)); SystemClassLoader.define(IO.toByteArray(loaderIn)); 然后在插件的onEnable方法里把这两个类define到AppClassLoader也就是JVM第三级类加载器 也可以在onLoad方法里define,反正都一样只要别在onDisable里define就好 嗷呜~ 因为每次插件onEnable都会define,重复加载相同的类会报错,需要捕捉一下异常,直接忽略掉 之后要改一下HyperPluginClassLoader的parent 还要改一下之前说过的parentClasses,这个Map在Bukkit的PluginClassLoader里 然后就可以onEnable你的插件本体啦  |  
  |  
  |  
 
 
 这里写的插件本体在服务器运行的过程里只会调用一次onLoad方法 虽然不知道有什么用,但是之前写fx的时候发现的这个方法就来水一贴了哈哈哈哈
 本体的主类要继承之前写的HyperPlugin而不是JavaPlugin - public class Main extends HyperPlugin
  - {
  -         @Override
  -         public void onLoad()
  -         {
  -                 System.out.println("Plugin load");
  -                 System.out.println(this.getClass().getClassLoader());
  -         }
  
 -         @Override
  -         public void onEnable()
  -         {
  -                 System.out.println("Plugin enable");
  -                 System.out.println(this.getClass().getClassLoader());
  -         }
  
 -         @Override
  -         public void onDisable()
  -         {
  -                 System.out.println("Plugin disable");
  -                 System.out.println(this.getClass().getClassLoader());
  -         }
  - }
 
  复制代码可以输出一下类加载器看看前后的类是不是唯一  |  
  |  
  |  
 
 
 第二节的Plugin编译的jar放在plugins文件夹里 第三节Core编译的jar放在之前类加载器指定的目录里 我指定的是plugins/HyperPlugin/Core.jar 当然你也可以改但是别忘记改类加载器里的路径 
 
 onLoad->onEnable->reload->onDisable->onEnable onLoad后面的onEnable我没截图,反正都一样
  这里输出的内存地址都一样说明插件在reload前后实例没有变 成功啦!嗷呜~  |  
  |  
  |  
 
 
 
 
 插件源码 
 这里IO工具和Reflect工具我也和源码一起丢出来了可以扒源码自己看 
 如果有什么不明白的地方可以来问我啦 嗷呜~ 
 
 
 
 
 
 又是一篇不会有人看的没什么用的教程呢
 
 
 
 
  |