--- front: hard: 进阶 time: 8分钟 --- # 便捷的反射工具 ## 介绍 TabooLib 内置了专属的反射依赖库 [https://github.com/TabooLib/reflex](https://github.com/TabooLib/reflex) Reflex 为基于 Kotlin 语言开发的反射工具,其与 Java 原生反射 API 及 kotlin-reflect 间最大区别在于其可无视软兼容反射目标类中的字段或方法。 你无需了解这么多 只需要看看有没有遇到这种情况: 1. 想获取一个private的变量 2. 想执行一个private的方法 ## 用法 ```kotlin class TestNoEdit(){ private var a = 0 private fun say(){ println("say") } } fun test(){ val test = TestNoEdit() test.a = 1 // 并不可以这样 // val property = test.getProperty("a") ?: 0 test.invokeMethod("say") TestNoEdit::class.java.invokeConstructor() } ``` 至此 成为反射的神! ## 获取字段参数 ```kotlin test.getProperty<字段类型>("字段的名称") // T: val property = test.getProperty("a") ?: 0 ``` 那,老师 如果我不知道用什么字段类型应该怎么办? 可以使用 Any ```kotlin test.getProperty("a") ``` ## 设置字段内容 ```kotlin test.setProperty("字段的名称","要设置的值") test.setProperty("a",10) ``` ## 运行方法 ```kotlin invokeConstructor(构造函数的类型) // 例如 构造函数需要两个字符串 invokeConstructor(String::class.java,String::class.java) ``` ## 获取构造函数 ```kotlin invokeConstructor(构造函数的类型) // 例如 构造函数需要两个字符串 invokeConstructor(String::class.java,String::class.java) ``` ## 案例 - 扫包读取某class的注解进行注册 6.1 ```kotlin @Awake object GermUIHook : ClassVisitor(5) { // 在什么时机进行扫描 override fun getLifeCycle(): LifeCycle { return LifeCycle.ENABLE } // 扫描类,同级的还有扫描方法 和 扫描常量的 override fun visitStart(clazz: Class<*>, instance: Supplier<*>?) { // 判断是否实现了 IGermUI 接口 if (clazz.interfaces.contains(IGermUI::class.java)) { // 判断是否这个类被 @HookGerm("value") 修饰 if (clazz.isAnnotationPresent(HookGerm::class.java)) { // 获取注解 @HookGerm val hook = clazz.getAnnotation(HookGerm::class.java) // 取出内容并添加到 Map中 hookMap[hook.value] = clazz as Class } else { val name = clazz.simpleName hookMap[name] = clazz as Class } } } } ``` ```kotlin /** * 当类开始加载时 * * @param clazz 类 * @param instance 实例 */ public void visitStart(@NotNull Class clazz, @Nullable Supplier instance) { } /** * 当类结束加载时 * * @param clazz 类 * @param instance 实例 */ public void visitEnd(@NotNull Class clazz, @Nullable Supplier instance) { } /** * 当字段加载时 * * @param field 字段 * @param clazz 类 * @param instance 实例 */ public void visit(@NotNull ClassField field, @NotNull Class clazz, @Nullable Supplier instance) { } /** * 当方法加载时 * * @param method 方法 * @param clazz 类 * @param instance 实例 */ public void visit(@NotNull ClassMethod method, @NotNull Class clazz, @Nullable Supplier instance) { } ``` ## 案例 - 扫包注册papi变量 6.2 ```kotlin @Awake @Inject class PlaceholderRegister : ClassVisitor(0) { val hooked by unsafeLazy { runCatching { Class.forName("me.clip.placeholderapi.expansion.PlaceholderExpansion") }.isSuccess } override fun visitStart(clazz: ReflexClass) { if (hooked && clazz.structure.interfaces.any { it.name == PlaceholderExpansion::class.java.name }) { val expansion = findInstance(clazz) as? PlaceholderExpansion ?: error("PlaceholderExpansion must have an instance") if (!expansion.enabled) { return } object : me.clip.placeholderapi.expansion.PlaceholderExpansion() { override fun persist(): Boolean { return true } override fun getIdentifier(): String { return expansion.identifier } override fun getAuthor(): String { return BukkitPlugin.getInstance().description.authors.toString() } override fun getVersion(): String { return BukkitPlugin.getInstance().description.version } override fun onPlaceholderRequest(player: Player?, params: String): String { return expansion.onPlaceholderRequest(player, params) } override fun onRequest(player: OfflinePlayer?, params: String): String { return expansion.onPlaceholderRequest(player, params) } }.also { papiExpansion -> // 自动重载 if (expansion.autoReload) { registerBukkitListener(ExpansionUnregisterEvent::class.java) { if (it.expansion == papiExpansion) { submit { papiExpansion.register() } } } } }.register() } } override fun getLifeCycle(): LifeCycle { return LifeCycle.ENABLE } } ```