虽然但是,为啥是自行车
Karlatemp 发布的帖子
-
RE: LuckPerms - Mirai - 高级权限服务插件
@cabbcat 我的设计中是能通过这种命令来基于 g** 的,但我并没有进行详细测试,如果有问题的话请发个issue,有时间我会去检查修复的
-
RE: 如何在载入插件之前加载第三方依赖
config.json直接写依赖,mcl会尝试去从 mirai-repo 获取版本信息,然而你的库在 mirai-repo 不存在,目前可用方法是直接把你的依赖直接扔进 plugins, 更好的依赖解决模式目前还未完成,见 https://github.com/mamoe/mirai/pull/1842
-
RE: 加载Gradle变更时出错
根据
Temp/sync.studio.tooling7.gradle
, 此为 IDEA 内部问题, 请前往 JetBrains YouTrack 请求帮助 -
RE: [求助] Mirai-Console 插件打包依赖问题
更换 kt 版本
plugins { kotlin("jvm") version "1.5.30" kotlin("plugin.serialization") version "1.5.30" }
-
Java 动态模块系统 | Java dynamic module system
前言
在使用高版本的时候, 总会不可避免的接触到模块系统, 比如反射操作
java.base
已经十分困难. 既然 JDK 内部可以享受到模块的保护, 那么我们自己的代码是否也可以享受到模块系统的保护呢当然可以,而且也不是非常麻烦。
使用模块,你将面对以下问题
- 得到反射保护, 外部代码将不能通过反射强行修改/调用私有成员
- 更严格的访问控制, 不能直接访问非 required 的模块
- 失去
--add-opens=....=ALL-UNNAMED
的归属判断 - 需要专门的 ClassLoader / 需要自行实现 ClassLoader
使用模块的适用情况
- 需要编写严格的附属(插件)系统
- 需要保护自身代码/保护自身内存空间
觉得弄着好玩
定义一个模块
注: 此处的定义指的是, 通过运行时代码在运行时定义一个模块.
而不是大多数资料说的直接写一个module-info.java
要定义一个模块, 首先需要一个模块的描述符文件 (
ModuleDescriptor
), 可以从以编码文件读取 (ModuleDescriptor.read(InputStream) <- module-info.class
), 也可以在运行时动态生成一个(ModuleDescriptor.newModule("name_of_module").build()
)jvm 通过包来区分模块, 而一个模块的全部包都需要提前指定, jvm 才会为这些包分配到一个模块内
var moduleDescriptor = ModuleDescriptor.newModule("my.custom_module") .packages(Set.of("io.github.karlatemp.jmse.main")) .exports("io.github.karlatemp.jmse.main") .build();
这里我们已经拥有了一个模块的描述符, 现在我们还需要一个模块描述的引用, 以及一个模块查找器以让 jvm 可以找到我们的模块
var myModuleReference = new ModuleReference( moduleDescriptor, null ) { @Override public ModuleReader open() throws IOException { throw new UnsupportedOperationException(); } } var myModuleFinder = new ModuleFinder() { @Override public Optional<ModuleReference> find(String name) { if (name.equals(moduleDescriptor.name())) { return Optional.of(myModuleReference); } return Optional.empty(); } @Override public Set<ModuleReference> findAll() { return Set.of(myModuleReference); } };
最后,定义一个模块
var bootLayer = ModuleLayer.boot(); var myConfiguration = bootLayer.configuration().resolve( myModuleFinder, ModuleFinder.of(), Set.of(moduleDescriptor.name()) ); var classLoader = ClassLoader.getSystemClassLoader(); var controller = ModuleLayer.defineModules( myConfiguration, List.of(bootLayer), $ -> classLoader ); Class.forName("io.github.karlatemp.jmse.main.ModuleMain", false, classLoader) .getMethod("launch") .invoke(null);
ServiceLoader / Class.forName(Module, String)
还记得
需要专门的 ClassLoader / 需要自行实现 ClassLoader
吗, 虽然在上文已经成功定义了一个模块,但是只要使用ServiceLoader
/Class.forName(Module, String)
, 那么将无法找到对应的类, 因为一般的 ClassLoader 并没有专门处理动态加载的模块Analyze
通过进行调用分析, 最终可以发现以上两个东西最终都进入到了下面的方法
public class ClassLoader { final Class<?> loadClass(Module module, String name) { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class<?> c = findLoadedClass(name); if (c == null) { c = findClass(module.getName(), name); } if (c != null && c.getModule() == module) { return c; } else { return null; } } } protected Class<?> findClass(String moduleName, String name) { if (moduleName == null) { try { return findClass(name); } catch (ClassNotFoundException ignore) { } } return null; } }
不难发现, 由于默认没有处理模块, 导致指定搜索模块的时候将搜索不到动态定义的模块
而
jdk.internal.loader.ClassLoader$AppClassLoader
并没有处理通过ModuleLayer.defineModule
定义的模块, 于是也不能直接将模块定义到系统类加载器自行实现类加载器
自行实现类加载器十分简单,只需要
public class MyCustomClassLoader extends URLClassLoader { String moduleName; @Override protected Class<?> findClass(String moduleName, String name) { // System.out.println("Find class: " + moduleName + "/" + name); if (this.moduleName.equals(moduleName)) { try { return findClass(name); } catch (ClassNotFoundException ignored) { } } return super.findClass(moduleName, name); } }
使用 JDK 内置的类加载器
只需要实现
ModuleReference.open(): ModuleReader
, 然后使用var controller = ModuleLayer.defineModulesWithOneLoader( myConfiguration, List.of(bootLayer), ClassLoader.getSystemClassLoader().getParent() ); var classLoader = controller.layer().findLoader(moduleDescriptor.name());
即可使用 JDK 内置的内加载器
完整参考