mirror of
https://github.com/HighCapable/KavaRef.git
synced 2025-09-10 12:34:24 +08:00
Initial commit
This commit is contained in:
64
docs-source/src/.vuepress/config.ts
Normal file
64
docs-source/src/.vuepress/config.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { defaultTheme } from 'vuepress';
|
||||
import { shikiPlugin } from '@vuepress/plugin-shiki';
|
||||
import { searchPlugin } from '@vuepress/plugin-search';
|
||||
import { navBarItems, sideBarItems, configs, pageLinkRefs } from './configs/template';
|
||||
import { env, markdown } from './configs/utils';
|
||||
|
||||
export default {
|
||||
dest: configs.dev.dest,
|
||||
port: configs.dev.port,
|
||||
base: configs.website.base,
|
||||
head: [['link', { rel: 'icon', href: configs.website.icon }]],
|
||||
title: configs.website.title,
|
||||
description: configs.website.locales['/en/'].description,
|
||||
locales: configs.website.locales,
|
||||
theme: defaultTheme({
|
||||
logo: configs.website.logo,
|
||||
repo: configs.github.repo,
|
||||
docsRepo: configs.github.repo,
|
||||
docsBranch: configs.github.branch,
|
||||
docsDir: configs.github.dir,
|
||||
editLinkPattern: ':repo/edit/:branch/:path',
|
||||
sidebar: sideBarItems,
|
||||
sidebarDepth: 2,
|
||||
locales: {
|
||||
'/en/': {
|
||||
navbar: navBarItems['/en/'],
|
||||
selectLanguageText: 'English (US)',
|
||||
selectLanguageName: 'English',
|
||||
editLinkText: 'Edit this page on GitHub',
|
||||
tip: 'Tips',
|
||||
warning: 'Notice',
|
||||
danger: 'Pay Attention',
|
||||
},
|
||||
'/zh-cn/': {
|
||||
navbar: navBarItems['/zh-cn/'],
|
||||
selectLanguageText: '简体中文 (CN)',
|
||||
selectLanguageName: '简体中文',
|
||||
editLinkText: '在 GitHub 上编辑此页',
|
||||
notFound: ['这里什么都没有', '我们怎么到这来了?', '这是一个 404 页面', '看起来我们进入了错误的链接'],
|
||||
backToHome: '回到首页',
|
||||
contributorsText: '贡献者',
|
||||
lastUpdatedText: '上次更新',
|
||||
tip: '小提示',
|
||||
warning: '注意',
|
||||
danger: '特别注意',
|
||||
openInNewWindow: '在新窗口中打开',
|
||||
toggleColorMode: '切换颜色模式'
|
||||
}
|
||||
},
|
||||
}),
|
||||
extendsMarkdown: (md: markdownit) => {
|
||||
markdown.injectLinks(md, env.dev ? pageLinkRefs.dev : pageLinkRefs.prod);
|
||||
},
|
||||
plugins: [
|
||||
shikiPlugin({ theme: 'github-dark-dimmed' }),
|
||||
searchPlugin({
|
||||
isSearchable: (page) => page.path !== '/',
|
||||
locales: {
|
||||
'/en/': { placeholder: 'Search' },
|
||||
'/zh-cn/': { placeholder: '搜索' }
|
||||
}
|
||||
})
|
||||
]
|
||||
};
|
148
docs-source/src/.vuepress/configs/template.ts
Normal file
148
docs-source/src/.vuepress/configs/template.ts
Normal file
@@ -0,0 +1,148 @@
|
||||
import { i18n } from './utils';
|
||||
|
||||
interface PageLinkRefs {
|
||||
dev: Record<string, string>[];
|
||||
prod: Record<string, string>[];
|
||||
}
|
||||
|
||||
const navigationLinks = {
|
||||
start: [
|
||||
'/guide/home',
|
||||
'/guide/quick-start'
|
||||
],
|
||||
library: [
|
||||
'/library/kavaref-core',
|
||||
'/library/kavaref-extension'
|
||||
],
|
||||
config: [
|
||||
'/config/r8-proguard',
|
||||
'/config/processor-resolvers',
|
||||
'/config/migration'
|
||||
],
|
||||
about: [
|
||||
'/about/changelog',
|
||||
'/about/future',
|
||||
'/about/contacts',
|
||||
'/about/about'
|
||||
]
|
||||
};
|
||||
|
||||
export const configs = {
|
||||
dev: {
|
||||
dest: 'dist',
|
||||
port: 9000
|
||||
},
|
||||
website: {
|
||||
base: '/KavaRef/',
|
||||
icon: '/KavaRef/images/logo.svg',
|
||||
logo: '/images/logo.svg',
|
||||
title: 'KavaRef',
|
||||
locales: {
|
||||
'/en/': {
|
||||
lang: 'en-US',
|
||||
description: 'A modernizing Java Reflection with Kotlin'
|
||||
},
|
||||
'/zh-cn/': {
|
||||
lang: 'zh-CN',
|
||||
description: '一个使用 Kotlin 实现的现代化 Java 反射'
|
||||
}
|
||||
}
|
||||
},
|
||||
github: {
|
||||
repo: 'https://github.com/HighCapable/KavaRef',
|
||||
page: 'https://highcapable.github.io/KavaRef',
|
||||
branch: 'main',
|
||||
dir: 'docs-source/src'
|
||||
}
|
||||
};
|
||||
|
||||
export const pageLinkRefs: PageLinkRefs = {
|
||||
dev: [
|
||||
{ 'repo://': `${configs.github.repo}/` },
|
||||
// KDoc URL for local debugging, non-fixed value, adjust according to your own needs.
|
||||
// You can run ./build-dokka.sh and start the local server in dist/KDoc.
|
||||
{ 'kdoc://': 'http://localhost:9001/' }
|
||||
],
|
||||
prod: [
|
||||
{ 'repo://': `${configs.github.repo}/` },
|
||||
{ 'kdoc://': `${configs.github.page}/KDoc/` }
|
||||
]
|
||||
};
|
||||
|
||||
export const navBarItems = {
|
||||
'/en/': [{
|
||||
text: 'Navigation',
|
||||
children: [{
|
||||
text: 'Get Started',
|
||||
children: i18n.array(navigationLinks.start, 'en')
|
||||
}, {
|
||||
text: 'Libraries',
|
||||
children: i18n.array(navigationLinks.library, 'en')
|
||||
}, {
|
||||
text: 'Configs',
|
||||
children: i18n.array(navigationLinks.config, 'en')
|
||||
}, {
|
||||
text: 'About',
|
||||
children: i18n.array(navigationLinks.about, 'en')
|
||||
}]
|
||||
}, {
|
||||
text: 'Contact Us',
|
||||
link: i18n.string(navigationLinks.about[2], 'en')
|
||||
}],
|
||||
'/zh-cn/': [{
|
||||
text: '导航',
|
||||
children: [{
|
||||
text: '入门',
|
||||
children: i18n.array(navigationLinks.start, 'zh-cn')
|
||||
}, {
|
||||
text: '依赖',
|
||||
children: i18n.array(navigationLinks.library, 'zh-cn')
|
||||
}, {
|
||||
text: '配置',
|
||||
children: i18n.array(navigationLinks.config, 'zh-cn')
|
||||
}, {
|
||||
text: '关于',
|
||||
children: i18n.array(navigationLinks.about, 'zh-cn')
|
||||
}]
|
||||
}, {
|
||||
text: '联系我们',
|
||||
link: i18n.string(navigationLinks.about[2], 'zh-cn')
|
||||
}]
|
||||
};
|
||||
|
||||
export const sideBarItems = {
|
||||
'/en/': [{
|
||||
text: 'Get Started',
|
||||
collapsible: true,
|
||||
children: i18n.array(navigationLinks.start, 'en')
|
||||
}, {
|
||||
text: 'Libraries',
|
||||
collapsible: true,
|
||||
children: i18n.array(navigationLinks.library, 'en')
|
||||
}, {
|
||||
text: 'Configs',
|
||||
collapsible: true,
|
||||
children: i18n.array(navigationLinks.config, 'en')
|
||||
}, {
|
||||
text: 'About',
|
||||
collapsible: true,
|
||||
children: i18n.array(navigationLinks.about, 'en')
|
||||
}],
|
||||
'/zh-cn/': [{
|
||||
text: '入门',
|
||||
collapsible: true,
|
||||
children: i18n.array(navigationLinks.start, 'zh-cn')
|
||||
}, {
|
||||
text: '依赖',
|
||||
collapsible: true,
|
||||
children: i18n.array(navigationLinks.library, 'zh-cn')
|
||||
}, {
|
||||
text: '配置',
|
||||
collapsible: true,
|
||||
children: i18n.array(navigationLinks.config, 'zh-cn')
|
||||
}, {
|
||||
text: '关于',
|
||||
collapsible: true,
|
||||
children: i18n.array(navigationLinks.about, 'zh-cn')
|
||||
}]
|
||||
};
|
39
docs-source/src/.vuepress/configs/utils.ts
Normal file
39
docs-source/src/.vuepress/configs/utils.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
export const env = {
|
||||
dev: process.env.NODE_ENV === 'development'
|
||||
};
|
||||
|
||||
export const i18n = {
|
||||
space: ' ',
|
||||
string: (content: string, locale: string) => {
|
||||
return '/' + locale + content;
|
||||
},
|
||||
array: (contents: string[], locale: string) => {
|
||||
const newContents: string[] = [];
|
||||
contents.forEach((content) => {
|
||||
newContents.push(i18n.string(content, locale));
|
||||
});
|
||||
return newContents;
|
||||
}
|
||||
};
|
||||
|
||||
export const markdown = {
|
||||
injectLinks: (md: markdownit, maps: Record<string, string>[]) => {
|
||||
const defaultRender = md.renderer.rules.link_open || function (tokens, idx, options, _env, self) {
|
||||
return self.renderToken(tokens, idx, options);
|
||||
};
|
||||
md.renderer.rules.link_open = function (tokens, idx, options, env, self) {
|
||||
const hrefIndex = tokens[idx].attrIndex('href');
|
||||
let current = tokens[idx].attrs!![hrefIndex][1];
|
||||
for (const map of maps) {
|
||||
for (const [search, replace] of Object.entries(map)) {
|
||||
if (current.startsWith(search)) {
|
||||
current = current.replace(search, replace);
|
||||
tokens[idx].attrs!![hrefIndex][1] = current;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return defaultRender(tokens, idx, options, env, self);
|
||||
};
|
||||
}
|
||||
};
|
11
docs-source/src/.vuepress/public/images/logo.svg
Normal file
11
docs-source/src/.vuepress/public/images/logo.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 13 KiB |
179
docs-source/src/.vuepress/styles/index.scss
Normal file
179
docs-source/src/.vuepress/styles/index.scss
Normal file
@@ -0,0 +1,179 @@
|
||||
$primary-color: rgb(59, 85, 113);
|
||||
$accent-color: rgb(71, 103, 137);
|
||||
$content-width: 965px;
|
||||
$scroll-bar-width: 8px;
|
||||
$scroll-bar-height: 6.5px;
|
||||
$scroll-bar-border-radius: 50px;
|
||||
$scroll-bar-track-color-code: rgb(86, 96, 110);
|
||||
$scroll-bar-thumb-hover-color-code: rgb(121, 135, 155);
|
||||
|
||||
:root {
|
||||
--c-brand: #{$primary-color};
|
||||
--c-brand-light: #{$accent-color};
|
||||
--content-width: #{$content-width};
|
||||
}
|
||||
|
||||
code {
|
||||
padding: 3px 5px 3px 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.badge {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.custom-container {
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.sidebar-item {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.language-text {
|
||||
::-webkit-scrollbar-track {
|
||||
background: #{$scroll-bar-track-color-code};
|
||||
border-radius: #{$scroll-bar-border-radius};
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #{$scroll-bar-thumb-hover-color-code};
|
||||
}
|
||||
}
|
||||
|
||||
.language-kotlin {
|
||||
::-webkit-scrollbar-track {
|
||||
background: #{$scroll-bar-track-color-code};
|
||||
border-radius: #{$scroll-bar-border-radius};
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #{$scroll-bar-thumb-hover-color-code};
|
||||
}
|
||||
}
|
||||
|
||||
.language-java {
|
||||
::-webkit-scrollbar-track {
|
||||
background: #{$scroll-bar-track-color-code};
|
||||
border-radius: #{$scroll-bar-border-radius};
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #{$scroll-bar-thumb-hover-color-code};
|
||||
}
|
||||
}
|
||||
|
||||
.language-groovy {
|
||||
::-webkit-scrollbar-track {
|
||||
background: #{$scroll-bar-track-color-code};
|
||||
border-radius: #{$scroll-bar-border-radius};
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #{$scroll-bar-thumb-hover-color-code};
|
||||
}
|
||||
}
|
||||
|
||||
.language-xml {
|
||||
::-webkit-scrollbar-track {
|
||||
background: #{$scroll-bar-track-color-code};
|
||||
border-radius: #{$scroll-bar-border-radius};
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #{$scroll-bar-thumb-hover-color-code};
|
||||
}
|
||||
}
|
||||
|
||||
.hidden-anchor-page {
|
||||
h6 {
|
||||
color: transparent;
|
||||
margin-bottom: -35px;
|
||||
padding-top: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
.code-page {
|
||||
h1 {
|
||||
font-size: 24pt;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 18pt;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 15pt;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 12pt;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 9.6pt;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 8.4pt;
|
||||
}
|
||||
|
||||
.symbol {
|
||||
color: rgb(142, 155, 168);
|
||||
}
|
||||
|
||||
.deprecated {
|
||||
color: rgb(142, 155, 168);
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: #{$scroll-bar-width};
|
||||
height: #{$scroll-bar-height};
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: rgb(234, 236, 239);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgb(189, 189, 189);
|
||||
border-radius: #{$scroll-bar-border-radius};
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: rgb(133, 133, 133);
|
||||
border-radius: #{$scroll-bar-border-radius};
|
||||
}
|
||||
}
|
||||
|
||||
html.dark {
|
||||
--c-brand: #{$primary-color};
|
||||
--c-brand-light: #{$accent-color};
|
||||
--content-width: #{$content-width};
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: #{$scroll-bar-width};
|
||||
height: #{$scroll-bar-height};
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: rgb(41, 46, 53);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgb(65, 72, 83);
|
||||
border-radius: #{$scroll-bar-border-radius};
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: rgb(56, 62, 72);
|
||||
border-radius: #{$scroll-bar-border-radius};
|
||||
}
|
||||
}
|
27
docs-source/src/en/about/about.md
Normal file
27
docs-source/src/en/about/about.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# About This Document
|
||||
|
||||
> This document is powered by [VuePress](https://v2.vuepress.vuejs.org/en).
|
||||
|
||||
## License
|
||||
|
||||
[Apache-2.0](https://github.com/HighCapable/KavaRef/blob/main/LICENSE)
|
||||
|
||||
```:no-line-numbers
|
||||
Apache License Version 2.0
|
||||
|
||||
Copyright (C) 2019 HighCapable
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
```
|
||||
|
||||
Copyright © 2019 HighCapable
|
29
docs-source/src/en/about/changelog.md
Normal file
29
docs-source/src/en/about/changelog.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Changelog
|
||||
|
||||
> The version update history of `KavaRef` is recorded here.
|
||||
|
||||
::: danger
|
||||
|
||||
We will only maintain the latest API version, if you are using an outdate API version, you voluntarily renounce any possibility of maintenance.
|
||||
|
||||
:::
|
||||
|
||||
::: warning
|
||||
|
||||
To avoid translation time consumption, Changelog will use **Google Translation** from **Chinese** to **English**, please refer to the original text for actual reference.
|
||||
|
||||
Time zone of version release date: **UTC+8**
|
||||
|
||||
:::
|
||||
|
||||
## kavaref-core
|
||||
|
||||
### 1.0.0 | 2025.06.25  <Badge type="tip" text="latest" vertical="middle" />
|
||||
|
||||
- The first version is submitted to Maven
|
||||
|
||||
## kavaref-extension
|
||||
|
||||
### 1.0.0 | 2025.06.25  <Badge type="tip" text="latest" vertical="middle" />
|
||||
|
||||
- The first version is submitted to Maven
|
16
docs-source/src/en/about/contacts.md
Normal file
16
docs-source/src/en/about/contacts.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Contact Us
|
||||
|
||||
> If you have any questions in use, or have any constructive suggestions, you can contact us.
|
||||
|
||||
Join our developers group.
|
||||
|
||||
- [Click to join Telegram group](https://t.me/KavaRef)
|
||||
- [Click to join Telegram group (Developer))](https://t.me/HighCapable_Dev)
|
||||
|
||||
Find me on **Twitter** [@fankesyooni](https://twitter.com/fankesyooni).
|
||||
|
||||
## Help with Maintenance
|
||||
|
||||
Thank you for choosing and using `KavaRef`.
|
||||
|
||||
If you have code-related suggestions and requests, you can submit a Pull Request on GitHub.
|
114
docs-source/src/en/about/future.md
Normal file
114
docs-source/src/en/about/future.md
Normal file
@@ -0,0 +1,114 @@
|
||||
# Looking for Future
|
||||
|
||||
> The future is bright and uncertain, let us look forward to the future development space of `KavaRef`.
|
||||
|
||||
## Future Plans
|
||||
|
||||
> Features that `KavaRef` may add later are included here.
|
||||
|
||||
### Supports Class Filtering through ClassLoader
|
||||
|
||||
`KavaRef` currently only supports search and calls for reflection APIs such as `Method`, `Field`, and `Constructor`.
|
||||
In the future, the ability to filter `Class` by specified type `ClassLoader` may be supported in Java and Android platforms according to requirements.
|
||||
|
||||
Currently, you can use [DexKit](https://github.com/LuckyPray/DexKit) to complete this requirement,
|
||||
which also supports more complex searches and calls of reflective APIs such as Method, Field, and Constructor.
|
||||
|
||||
### Automatically Generate Reflection Code
|
||||
|
||||
**This is a feature that has been initially established in [YukiReflection](https://github.com/HighCapable/YukiReflection), and `KavaRef` is ready to continue to implement it in the possible time in the future.**
|
||||
|
||||
Use `stub` to create a Kotlin class, and declare the parameters in it, as well as its different states in each version.
|
||||
|
||||
For example, the Java class below is the target class we need to reflect.
|
||||
|
||||
> The following example
|
||||
|
||||
```java:no-line-numbers
|
||||
package com.example.test;
|
||||
|
||||
public class MyClass {
|
||||
|
||||
private String myField = "test";
|
||||
|
||||
public MyClass() {
|
||||
//...
|
||||
}
|
||||
|
||||
private String myMethod1(String var1, int var2) {
|
||||
//...
|
||||
}
|
||||
|
||||
private void myMethod2() {
|
||||
//...
|
||||
}
|
||||
|
||||
private void myMethod3(String var1) {
|
||||
//...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Through the existing usage of the current API, this class can be called reflectively in the following way.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
MyClass().resolve().apply {
|
||||
// Call myField.
|
||||
val value = firstField { name = "myField" }.get<String>()
|
||||
// Call myMethod1.
|
||||
val methodValue = firstMethod { name = "myMethod1" }.invoke<String>("test", 0)
|
||||
// Call myMethod2.
|
||||
firstMethod { name = "myMethod2" }.invoke()
|
||||
// Call myMethod3.
|
||||
firstMethod { name = "myMethod3" }.invoke("test")
|
||||
}
|
||||
```
|
||||
|
||||
The function to be implemented at present can be directly defined as the following Kotlin class using the reflection function.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
package com.example.test
|
||||
|
||||
@ReflectClass
|
||||
class MyClass {
|
||||
|
||||
@ReflectField
|
||||
val myField: String = fieldValueOf("none")
|
||||
|
||||
@ReflectMethod
|
||||
fun myMethod1(var1: String, var2: Int): String = methodReturnValueOf("none")
|
||||
|
||||
@ReflectMethod
|
||||
fun myMethod2() = MethodReturnType.Unit
|
||||
|
||||
@ReflectMethod
|
||||
fun myMethod3(var1: String) = MethodReturnType.Unit
|
||||
}
|
||||
```
|
||||
|
||||
Then we can directly call this defined Kotlin class to implement the reflection function, and the API will automatically generate the reflection code according to the annotation.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
MyClass().also {
|
||||
// Call myField
|
||||
val value = it.myField
|
||||
// Call myMethod1
|
||||
val methodValue = it.myMethod1("test", 0)
|
||||
// Call myMethod2
|
||||
it.myMethod2()
|
||||
// Call myMethod3
|
||||
it.myMethod3("test")
|
||||
}
|
||||
```
|
||||
|
||||
::: tip
|
||||
|
||||
The above functions may change after the actual release, and the functions of the actual version shall prevail.
|
||||
|
||||
:::
|
266
docs-source/src/en/config/migration.md
Normal file
266
docs-source/src/en/config/migration.md
Normal file
@@ -0,0 +1,266 @@
|
||||
# Migration to KavaRef
|
||||
|
||||
If you are used to using the reflection API in [YukiReflection](https://github.com/HighCapable/YukiReflection) or [YukiHookAPI](https://github.com/HighCapable/YukiHookAPI), you can refer to the following to migrate to `KavaRef`.
|
||||
|
||||
::: warning
|
||||
|
||||
For `YukiHookAPI`, you need to continue using its Hook API, and `KavaRef` only includes Java reflection-related APIs.
|
||||
|
||||
:::
|
||||
|
||||
## Basic Functions
|
||||
|
||||
The design concept of `KavaRef` is similar to `YukiReflection`, but not exactly the same.
|
||||
The following lists the differences between `YukiReflection` and `KavaRef` in basic reflection functions, which you can manually migrate based on.
|
||||
|
||||
For example, we have the following Java class.
|
||||
|
||||
> The following example
|
||||
|
||||
```java :no-line-numbers
|
||||
public class MyClass {
|
||||
|
||||
private void myMethod(String content) {
|
||||
System.out.println("Hello " + content + "!");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here is a comparison of `KavaRef` with `YukiReflection` using examples.
|
||||
|
||||
> The following example
|
||||
|
||||
<div style="display: flex; gap: 16px;">
|
||||
|
||||
<div style="flex: 1;">
|
||||
<h4>KavaRef</h4>
|
||||
|
||||
```kotlin
|
||||
// Assume that's your MyClass instance.
|
||||
val myClass: MyClass
|
||||
// Call and execute using KavaRef.
|
||||
MyClass::class.resolve().firstMethod {
|
||||
name = "myMethod"
|
||||
parameters(String::class)
|
||||
}.of(myClass).invoke("Hello, KavaRef!")
|
||||
// Direct reference to instance.
|
||||
myClass.resolve().firstMethod {
|
||||
name = "myMethod"
|
||||
parameters(String::class)
|
||||
}.invoke("Hello, KavaRef!")
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
<div style="flex: 1;">
|
||||
<h4>YukiReflection</h4>
|
||||
|
||||
```kotlin
|
||||
// Assume that's your MyClass instance.
|
||||
val myClass: MyClass
|
||||
// Call and execute using YukiReflection.
|
||||
MyClass::class.java.method {
|
||||
name = "myMethod"
|
||||
param(StringClass)
|
||||
}.get(myClass).call("Hello, YukiReflection!")
|
||||
// Direct reference to instance.
|
||||
myClass.current().method {
|
||||
name = "myMethod"
|
||||
param(StringClass)
|
||||
}.call("Hello, YukiReflection!")
|
||||
```
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
`KavaRef` starts reflection at any time, you need to use `resolve()` to create a reflection scope.
|
||||
You no longer directly extend the related `method` and `constructor` methods to avoid contaminating their scope.
|
||||
|
||||
`KavaRef` abandons the "Finder" design concept and uses the "Filter" design concept to obtain reflected results.
|
||||
"Find" is no longer a finding, but a "filtering".
|
||||
|
||||
`KavaRef` canceled the `YukiReflection` defined in the resulting instance whether the `Member` obtained is a multiple or a single design scheme,
|
||||
and directly returns the entire `List<MemberResolver>`.
|
||||
The example you see above uses `firstMethod` to get the first match `MethodResolver`,
|
||||
if you need to get all matches, you can change to `method`.
|
||||
|
||||
The conditional method name of `KavaRef` in `MethodCondition` has been modified from abbreviation
|
||||
such as `param` before `YukiReflection` to `parameters` to more in line with the naming habit of Java reflection API.
|
||||
|
||||
`KavaRef` no longer provides the `param(...).order()` function in the condition, because this function itself is unstable.
|
||||
`KavaRef` now uses an iterator for filtering, and the bytecode will no longer be in order, and the bytecode should not be filtered in order.
|
||||
You can use `firstMethod`, `firstField`, or `lastMethod`, `lastField`, etc. to get the first or last match result.
|
||||
|
||||
`KavaRef` renames the `get(instance)` method to `of(instance)` because `get(...)` may be confused with the `get(...)` usage of `Field` and is not semantic,
|
||||
At the same time, `get(instance)` is no longer getting the `MethodFinder.Result.Instance` instance from something like `MethodFinder.Result`,
|
||||
but uses `of(instance)` to always operate and set the instance object to `MemberResolver`.
|
||||
|
||||
Methods such as `string()`, `int()`, etc. in `MethodFinder.Result.Instance` have been removed in `KavaRef`.
|
||||
You can directly use `get<String>()`, `get<Int>()`, `invoke<String>(...)`, `invoke<Int>(...)`, etc. to get or call the corresponding type results.
|
||||
|
||||
::: danger
|
||||
|
||||
If you are looking for (filtering) `Field`, you need to note that there may be semantic conflicts between `KavaRef` and `YukiReflection` in the acquisition method of `Field`.
|
||||
Please pay special attention when migrating this part.
|
||||
|
||||
For example, get the static field of `content` in `MyClass`, in `YukiReflection`, you would do this.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
MyClass::class.java
|
||||
.field { name = "content" } // Return FieldFinder.Result
|
||||
.get() // Cannot be omitted, return FieldFinder.Result.Instance
|
||||
.string() // value
|
||||
```
|
||||
|
||||
In `KavaRef` you need to do this.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
MyClass::class.resolve()
|
||||
.firstField { name = "content" } // Return FieldResolver<MyClass>
|
||||
.get<String>() // value
|
||||
```
|
||||
|
||||
As mentioned above, `get(...)` is used to get the `FieldFinder.Result.Instance` object in Y`ukiReflection`, not the value.
|
||||
To get the value and process it as a specified type, you need to call `string()` or `cast<String>()`, and in `KavaRef`,
|
||||
you use `get<T>()` directly in `MemberResolver` to get the value of the specified type.
|
||||
The usage of `get(...)` of `KavaRef` for `get(...)` to `of(...)`.
|
||||
|
||||
So the complete writing of the above example in `KavaRef` should be.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Since the call is a static instance, "of(null)" can be omitted.
|
||||
MyClass::class.resolve()
|
||||
.firstField { name = "content" } // It's already a call chain object FieldResolver<MyClass>
|
||||
.of(null) // Can be omitted and return to the call chain object FieldResolver<MyClass>
|
||||
.get<String>() // value
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
`KavaRef` no longer provides `call` methods for `Method`, and is now merged uniformly into `invoke` (with generic parameters).
|
||||
At the same time, `KavaRef` defines the `newInstance` method of `Constructor` as `create` (with generic parameters).
|
||||
|
||||
You may have noticed that the condition `superClass()` is gone, it is still there,
|
||||
in `KavaRef` it has been renamed to `superclass()`, docking with the standard Java reflection API.
|
||||
|
||||
At the same time, `KavaRef` extends `KClass`, and you no longer need to use `Some::class.java` to declare an instance of `Class` in most scenarios.
|
||||
|
||||
Another design idea of `KavaRef` is type safety.
|
||||
As long as you use `KClass<T>` and `Class<T>` that declare the generic type, it will be checked and converted to the
|
||||
corresponding type when `of(instance)` and `create(...)`, and type checking will be completed during coding to avoid runtime errors.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Assume that's your MyClass instance.
|
||||
val myClass: MyClass
|
||||
// Using KavaRef to call and execute.
|
||||
MyClass::class
|
||||
.resolve()
|
||||
.firstMethod {
|
||||
name = "myMethod"
|
||||
parameters(String::class)
|
||||
}
|
||||
// Only instances of type MyClass can be passed in.
|
||||
.of(myClass)
|
||||
.invoke("Hello, KavaRef!")
|
||||
```
|
||||
|
||||
## Other Functions
|
||||
|
||||
`KavaRef` and `YukiReflection` are not much different in other functions and extended functions.
|
||||
`KavaRef` separates these functions into a separate module.
|
||||
|
||||
The following functionality is provided in `YukiReflection` but is not implemented and no longer provided in `KavaRef`:
|
||||
|
||||
- Preset reflection type constant classes, such as `StringClass`, `IntType`, etc
|
||||
- You can use Kotlin class references such as `String::class`, `Int::class`, etc. to replace it.
|
||||
For primitive types and wrapper classes, `IntType` is equivalent to `Int::class`, and `IntClass` is equivalent to `JInteger::class`
|
||||
|
||||
- `DexClassFinder` function
|
||||
- Due to its design flaws and possible performance issues when used on Android platforms, it is no longer available for now
|
||||
|
||||
- `RemedyPlan` and `method { ... } .remedys { ... }` functions
|
||||
- Due to possible black box problems, maintenance is relatively difficult.
|
||||
If you need to use similar functions, please implement them manually and will no longer be provided
|
||||
|
||||
- `ClassLoader.listOfClasses()` function
|
||||
- Due to the complex and unstable implementation solutions of each platform, it will no longer be provided
|
||||
|
||||
- `ClassLoader.searchClass()` function
|
||||
- Due to performance issues and design time is limited to Android platform,
|
||||
it is relatively difficult to maintain filter conditions and is no longer provided
|
||||
|
||||
- `Class.hasExtends`, `Class.extends`, `Class.implements` functions
|
||||
- You can replace them with `A::class isSubclassOf B::class`
|
||||
|
||||
- `Class.toJavaPrimitiveType()` function
|
||||
- There is conceptual confusion in functional design and will no longer be provided
|
||||
|
||||
- `"com.some.clazz".hasClass(loader)` function
|
||||
- You can use `loader.hasClass("com.some.clazz")` to replace it
|
||||
|
||||
- `Class.hasField`, `Class.hasMethod`, `Class.hasConstructor` functions
|
||||
- Due to design defects, no longer provided
|
||||
|
||||
- `Class.hasModifiers(...)`, `Member.hasModifiers(...)` functions
|
||||
- You can replace them directly with extension methods such as `Class.isPublic`, `Member.isPublic`
|
||||
|
||||
- `Class.generic()`, `GenericClass` functions
|
||||
- If you just want to get generic parameters of the superclass, you can use `Class.genericSuperclassTypeArguments()`.
|
||||
Due to design defects, no longer provided
|
||||
|
||||
- `Class.current()`, `CurrentClass` functions
|
||||
- Merged into the core function of `KavaRef.resolve()` and is no longer provided separately
|
||||
|
||||
- `Class.buildOf(...)` function
|
||||
- You can use `Class.createInstance(...)` to replace it
|
||||
|
||||
- `Class.allMethods()`, `Class.allFields()`, `Class.allConstructors()` functions
|
||||
- Due to its pollution scope, no longer provided
|
||||
|
||||
- `YLog` log function
|
||||
- `KavaRef` no longer takes over the logs, you can use the corresponding platform implementation method and no longer provided
|
||||
|
||||
## Exception Handling
|
||||
|
||||
`KavaRef` is completely different from `YukiReflection` in exception handling.
|
||||
The exception logic of `KavaRef` will remain transparent by default. <u>**It no longer actively intercepts exceptions and prints error logs or even provides `onNoSuchMethod` listener**</u>.
|
||||
When no valid members are filtered, `KavaRef` will throw an exception directly unless you **explicitly declare the condition as optional (consistent with `YukiReflection` logic)**.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Assume that's your MyClass instance.
|
||||
val myClass: MyClass
|
||||
// Using KavaRef to call and execute.
|
||||
MyClass::class
|
||||
.resolve()
|
||||
.optional() // Declare as optional, do not throw exceptions.
|
||||
// Use firstMethodOrNull instead of firstMethod,
|
||||
// because the NoSuchElementException of Kotlin itself will be thrown.
|
||||
.firstMethodOrNull {
|
||||
name = "doNonExistentMethod" // Assume that this method does not exist.
|
||||
parameters(String::class)
|
||||
}?.of(myClass)?.invoke("Hello, KavaRef!")
|
||||
```
|
||||
|
||||
For more information, please refer to the [Exception Handling](../library/kavaref-core.md#exception-handling) section in [kavaref-core](../library/kavaref-core.md).
|
||||
|
||||
## New to KavaRef
|
||||
|
||||
If you haven't used `YukiReflection` or `YukiHookAPI`, it doesn't matter, you can refer to the following content to get started quickly.
|
||||
|
||||
::: tip What to Do Next
|
||||
|
||||
For more information, please continue reading [kavaref-core](../library/kavaref-core.md) and [kavaref-extension](../library/kavaref-extension.md).
|
||||
|
||||
Get started using `KavaRef` now!
|
||||
|
||||
:::
|
37
docs-source/src/en/config/processor-resolvers.md
Normal file
37
docs-source/src/en/config/processor-resolvers.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Third-party Member Resolvers
|
||||
|
||||
> Here are some third-party Member resolvers for reference and use.
|
||||
>
|
||||
> Please read [Custom Resolver](../library/kavaref-core.md#custom-resolver) for usage.
|
||||
|
||||
## AndroidHiddenApiBypass
|
||||
|
||||
[Project URL](https://github.com/LSPosed/AndroidHiddenApiBypass)
|
||||
|
||||
> LSPass: Bypass restrictions on non-SDK interfaces
|
||||
|
||||
```kotlin
|
||||
class AndroidHiddenApiBypassResolver : MemberProcessor.Resolver() {
|
||||
|
||||
override fun <T : Any> getDeclaredConstructors(declaringClass: Class<T>): List<Constructor<T>> {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
||||
return super.getDeclaredConstructors(declaringClass)
|
||||
}
|
||||
|
||||
val constructors = HiddenApiBypass.getDeclaredMethods(declaringClass)
|
||||
.filterIsInstance<Constructor<T>>()
|
||||
.toList()
|
||||
return constructors
|
||||
}
|
||||
|
||||
override fun <T : Any> getDeclaredMethods(declaringClass: Class<T>): List<Method> {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
||||
return super.getDeclaredMethods(declaringClass)
|
||||
}
|
||||
|
||||
val methods = HiddenApiBypass.getDeclaredMethods(declaringClass)
|
||||
.filterIsInstance<Method>()
|
||||
.toList()
|
||||
return methods
|
||||
}
|
||||
}
|
6
docs-source/src/en/config/r8-proguard.md
Normal file
6
docs-source/src/en/config/r8-proguard.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# R8 & Proguard Obfuscate
|
||||
|
||||
> In most scenarios, Android application installation packages can compress volumes through obfuscation.
|
||||
> Here is a configuration method for obfuscation rules.
|
||||
|
||||
`KavaRef` does not require any additional configuration of obfuscation rules in Android projects.
|
46
docs-source/src/en/guide/home.md
Normal file
46
docs-source/src/en/guide/home.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Introduce
|
||||
|
||||
> `KavaRef` is a modern Java reflection API implemented using Kotlin.
|
||||
|
||||
## Background
|
||||
|
||||
This is a modern Java reflection API implemented using Kotlin, designed to provide a cleaner and easier-to-use API while retaining the power of Java
|
||||
reflection.
|
||||
|
||||
The project icon is designed by [MaiTungTM](https://github.com/Lagrio) and is named from **K**otlinJ**avaRef**lection, meaning Java reflection
|
||||
implemented using Kotlin.
|
||||
|
||||
It was firstborn in the [YukiHookAPI](https://github.com/HighCapable/YukiHookAPI), and was later decoupled into
|
||||
the [YukiReflection](https://github.com/HighCapable/YukiReflection) project.
|
||||
|
||||
As you can see, now `KavaRef` is a completely new set of APIs completely refactored with the design idea of `YukiReflection`,
|
||||
which has no affiliation and will replace `YukiReflection` as a new reflection solution.
|
||||
|
||||
If you are using `YukiReflection` or the `YukiHookAPI` project related to it, you can refer to [here](../config/migration) to migrate the reflection API to `KavaRef`.
|
||||
|
||||
## Usage
|
||||
|
||||
`KavaRef` is built in Kotlin **lambda** syntax with Java Builder style.
|
||||
|
||||
It can replace [Java's native Reflection API](https://www.oracle.com/technical-resources/articles/java/javareflection.html) and implement a more complete reflection solution in a more human-friendly language.
|
||||
|
||||
## Skill Requirements
|
||||
|
||||
You must be proficient in Java's native reflection APIs, understand Java's class loading mechanisms, bytecode structures, and how they are used in Kotlin (if you are using Kotlin).
|
||||
|
||||
## Language Requirement
|
||||
|
||||
It is recommended to use Kotlin. API code composition also supports Java,
|
||||
but in pure Java projects `KavaRef` may not be able to play its full functionality and syntactic sugar advantages.
|
||||
|
||||
All the demo sample code in the documentation will be described first using Kotlin.
|
||||
If you don't know how to use Kotlin at all, you may not be able to experience and use the functionality of `KavaRef` more fully.
|
||||
|
||||
## Contribution
|
||||
|
||||
The maintenance of this project is inseparable from the support and contributions of all developers.
|
||||
|
||||
This project is currently in its early stages, and there may still be some problems or lack of functions you need.
|
||||
|
||||
If possible, feel free to submit a PR to contribute features you think are needed to this project or goto [GitHub Issues](repo://issues)
|
||||
to make suggestions to us.
|
97
docs-source/src/en/guide/quick-start.md
Normal file
97
docs-source/src/en/guide/quick-start.md
Normal file
@@ -0,0 +1,97 @@
|
||||
# Quick Start
|
||||
|
||||
> Integrate `KavaRef` into your project.
|
||||
|
||||
## Project Requirements
|
||||
|
||||
The project needs to be created using `IntelliJ IDEA` or `Android Studio` and be of type Java or Android
|
||||
project and have integrated Kotlin environment dependencies.
|
||||
|
||||
- IntelliJ IDEA (It is recommended to get the latest version [from here](https://www.jetbrains.com/idea))
|
||||
|
||||
- Android Studio (It is recommended to get the latest version [from here](https://developer.android.com/studio))
|
||||
|
||||
- Kotlin 1.9.0+, Gradle 8+, Java 17+
|
||||
|
||||
### Configure Repositories
|
||||
|
||||
The dependencies of `KavaRef` are published in **Maven Central** and our public repository,
|
||||
you can use the following method to configure repositories.
|
||||
|
||||
We recommend using Kotlin DSL as the Gradle build script language and [SweetDependency](https://github.com/HighCapable/SweetDependency)
|
||||
to manage dependencies.
|
||||
|
||||
#### SweetDependency (Recommended)
|
||||
|
||||
Configure repositories in your project's `SweetDependency` configuration file.
|
||||
|
||||
```yaml
|
||||
repositories:
|
||||
google:
|
||||
maven-central:
|
||||
# (Optional) You can add this URL to use our public repository
|
||||
# When Sonatype-OSS fails and cannot publish dependencies, this repository is added as a backup
|
||||
# For details, please visit: https://github.com/HighCapable/maven-repository
|
||||
highcapable-maven-releases:
|
||||
url: https://raw.githubusercontent.com/HighCapable/maven-repository/main/repository/releases
|
||||
```
|
||||
|
||||
#### Traditional Method
|
||||
|
||||
Configure repositories in your project `build.gradle.kts`.
|
||||
|
||||
```kotlin
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
// (Optional) You can add this URL to use our public repository
|
||||
// When Sonatype-OSS fails and cannot publish dependencies, this repository is added as a backup
|
||||
// For details, please visit: https://github.com/HighCapable/maven-repository
|
||||
maven("https://raw.githubusercontent.com/HighCapable/maven-repository/main/repository/releases")
|
||||
}
|
||||
```
|
||||
|
||||
### Configure Java Version
|
||||
|
||||
Modify the Java version of Kotlin in your project `build.gradle.kts` to 17 or above.
|
||||
|
||||
> Java Project
|
||||
|
||||
```kt
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(17)
|
||||
}
|
||||
```
|
||||
|
||||
> Android Project
|
||||
|
||||
```kt
|
||||
android {
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Functional Overview
|
||||
|
||||
The project is divided into multiple modules. You can choose the module you wish to include as a dependency in your project, but be sure to include the **kavaref-core** module.
|
||||
|
||||
Click the corresponding module below to view detailed feature descriptions.
|
||||
|
||||
- [kavaref-core](../library/kavaref-core.md)
|
||||
- [kavaref-extension](../library/kavaref-extension.md)
|
||||
|
||||
## Demo
|
||||
|
||||
You can find some samples [here](repo://tree/main/samples) view the corresponding demo project to better understand how these functions work and quickly
|
||||
select the functions you need.
|
38
docs-source/src/en/index.md
Normal file
38
docs-source/src/en/index.md
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
home: true
|
||||
title: Home
|
||||
heroImage: /images/logo.svg
|
||||
actions:
|
||||
- text: Get Started
|
||||
link: /en/guide/home
|
||||
type: primary
|
||||
- text: Changelog
|
||||
link: /en/about/changelog
|
||||
type: secondary
|
||||
features:
|
||||
- title: Light and Elegant
|
||||
details: A powerful, elegant, beautiful API built with Kotlin lambda can help you quickly implement bytecode filtering and reflection functions.
|
||||
- title: Fully Compatible
|
||||
details: Using native Java APIs to implement reflection functionality, it can be used on any Kotlin on JVM project, and it is no problem on Android.
|
||||
- title: Quickly Started
|
||||
details: Simple and easy to use it now! Do not need complex configuration and full development experience, Integrate dependencies and enjoy yourself.
|
||||
footer: Apache-2.0 License | Copyright (C) 2019 HighCapable
|
||||
---
|
||||
|
||||
### Start reflecting anytime, anywhere.
|
||||
|
||||
```java
|
||||
public class World {
|
||||
|
||||
private void sayHello(String content) {
|
||||
System.out.println("Hello " + content + "!");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```kotlin
|
||||
World().resolve().firstMethod {
|
||||
name = "sayHello"
|
||||
parameters(String::class)
|
||||
}.invoke("KavaRef")
|
||||
```
|
790
docs-source/src/en/library/kavaref-core.md
Normal file
790
docs-source/src/en/library/kavaref-core.md
Normal file
@@ -0,0 +1,790 @@
|
||||
# kavaref-core
|
||||
|
||||

|
||||
<span style="margin-left: 5px"/>
|
||||

|
||||
|
||||
This is the core dependency of KavaRef, and you need to introduce this module to use the basic features of KavaRef.
|
||||
|
||||
## Configure Dependency
|
||||
|
||||
You can add this module to your project using the following method.
|
||||
|
||||
### SweetDependency (Recommended)
|
||||
|
||||
Add dependency in your project's `SweetDependency` configuration file.
|
||||
|
||||
```yaml
|
||||
libraries:
|
||||
com.highcapable.kavaref:
|
||||
kavaref-core:
|
||||
version: +
|
||||
```
|
||||
|
||||
Configure dependency in your project `build.gradle.kts`.
|
||||
|
||||
```kotlin
|
||||
implementation(com.highcapable.kavaref.kavaref.core)
|
||||
```
|
||||
|
||||
### Version Catalog
|
||||
|
||||
Add dependency in your project's `gradle/libs.versions.toml`.
|
||||
|
||||
```toml
|
||||
[versions]
|
||||
kavaref-core = "<version>"
|
||||
|
||||
[libraries]
|
||||
kavaref-core = { module = "com.highcapable.kavaref:kavaref-core", version.ref = "kavaref-core" }
|
||||
```
|
||||
|
||||
Configure dependency in your project `build.gradle.kts`.
|
||||
|
||||
```kotlin
|
||||
implementation(libs.kavaref.core)
|
||||
```
|
||||
|
||||
Please change `<version>` to the version displayed at the top of this document.
|
||||
|
||||
### Traditional Method
|
||||
|
||||
Configure dependency in your project `build.gradle.kts`.
|
||||
|
||||
```kotlin
|
||||
implementation("com.highcapable.kavaref:kavaref-core:<version>")
|
||||
```
|
||||
|
||||
Please change `<version>` to the version displayed at the top of this document.
|
||||
|
||||
## Function Introduction
|
||||
|
||||
You can view the KDoc [click here](kdoc://kavaref-core).
|
||||
|
||||
### Basic Usage
|
||||
|
||||
KavaRef adopts a chained call design, which creates extension methods for available Java reflection APIs (such as `Class`).
|
||||
You only need to call `resolve()` to these contents to enter the world of KavaRef.
|
||||
|
||||
The relationship diagram is as follows.
|
||||
|
||||
``` :no-line-numbers
|
||||
KavaRef
|
||||
└── KClass/Class/Any.resolve()
|
||||
├── method()
|
||||
├── constructor()
|
||||
└── field()
|
||||
```
|
||||
|
||||
Next, we will give multiple examples of Java `Class`, which will be explained based on them in the future.
|
||||
|
||||
```java :no-line-numbers
|
||||
package com.demo;
|
||||
|
||||
public class BaseTest {
|
||||
|
||||
public BaseTest() {
|
||||
// ...
|
||||
}
|
||||
|
||||
private void doBaseTask(String taskName) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```java :no-line-numbers
|
||||
package com.demo;
|
||||
|
||||
public class Test extends BaseTest {
|
||||
|
||||
private Test() {
|
||||
// ...
|
||||
}
|
||||
|
||||
private static TAG = "Test";
|
||||
|
||||
private boolean isTaskRunning = false;
|
||||
|
||||
private void doTask(String taskName) {
|
||||
// ...
|
||||
}
|
||||
|
||||
private void release(String taskName, Function<boolean, String> task, boolean isFinish) {
|
||||
// ...
|
||||
}
|
||||
|
||||
private void stop() {
|
||||
// ...
|
||||
}
|
||||
|
||||
private String getName() {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
public class Box<T> {
|
||||
|
||||
public void print(T item, String str) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Suppose, we want to get the `doTask` method of `Test` and execute it. In KavaRef, you can do it in the following ways.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Suppose this is an instance of this Class.
|
||||
val test: Test
|
||||
//Reflect it by instantiating Class.
|
||||
// In KavaRef you don't need to convert it to `java.lang.Class`,
|
||||
// It will automatically call KClass.java.
|
||||
Test::class
|
||||
// Create KavaRef reflections.
|
||||
.resolve()
|
||||
// Create method condition.
|
||||
.method {
|
||||
// Set method name.
|
||||
name = "doTask"
|
||||
// Set method parameter type.
|
||||
parameters(String::class)
|
||||
}
|
||||
// After the condition is executed, the matching List<MethodResolver>
|
||||
// instance will be returned.
|
||||
// Here we get the first filtering result.
|
||||
.first()
|
||||
// Setting an instance of Test on MethodResolver.
|
||||
.of(test)
|
||||
// Calling methods and passing in parameters.
|
||||
.invoke("task_name")
|
||||
```
|
||||
|
||||
In the above writing method, we use `Test::class.resolve()` to get the KavaRef reflection instance of the current `Class`.
|
||||
Then create a method filtering condition `MethodCondition` by `method { ... }`, which sets the method name and parameter type, and returns the `List<MethodResolver>` instance after execution.
|
||||
Then we use `first()` to get the first matched `MethodResolver` instance,
|
||||
Then use `of(test)` to set the current instance of `Class`, and finally use `invoke("task_name")` to execute the method and pass in the parameters.
|
||||
|
||||
In this, MethodCondition is inherited from MemberCondition, which allows you to conditionally filter the Method, which contains a conditional image of the Java-core reflection API.
|
||||
You can view the corresponding comments to understand the native usage of each API.
|
||||
|
||||
Similarly, MethodResolver is inherited from `MemberResolver`, which allows you to make reflection calls to `Method` in the filtered result.
|
||||
|
||||
Since the reflection requirement here is to obtain a available method result, the call chain of `method { ... }.first()` may be more cumbersome,
|
||||
and at this time there is the following simplification solution.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
Test::class
|
||||
.resolve()
|
||||
// Use firstMethod directly to get the first matching
|
||||
// MethodResolver instance.
|
||||
.firstMethod {
|
||||
name = "doTask"
|
||||
parameters(String::class)
|
||||
}
|
||||
.of(test)
|
||||
.invoke("task_name")
|
||||
```
|
||||
|
||||
Since we can now get an instance of `Test`, there is also a simplified way to write it,
|
||||
you can use this instance to create KavaRef reflections directly.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Here, the test instance test will be passed to
|
||||
// KavaRef and get test::class.java.
|
||||
test.resolve()
|
||||
.firstMethod {
|
||||
name = "doTask"
|
||||
parameters(String::class)
|
||||
} // Since you set up an instance, of(test) is no longer needed here.
|
||||
.invoke("task_name")
|
||||
```
|
||||
|
||||
Next, we need to get the `isTaskRunning` variable, which can be written in the following form.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Suppose this is an example of this Class.
|
||||
val test: Test
|
||||
// Call and execute with KavaRef.
|
||||
val isTaskRunning = test.resolve()
|
||||
.firstField {
|
||||
name = "isTaskRunning"
|
||||
type = Boolean::class
|
||||
}.get<Boolean>()
|
||||
```
|
||||
|
||||
The constructor in `Test` is privatized, and now we can create an instance of it using the following method.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
val test = Test::class.resolve()
|
||||
.firstConstructor {
|
||||
// For the zero parameter construction method,
|
||||
// the following conditions can be used to filter.
|
||||
// It is equivalent to parameterCount = 0.
|
||||
emptyParameters()
|
||||
}.create() // Create a new Test instance.
|
||||
```
|
||||
|
||||
You can also use `createAsType<T>()` to specify its superclass type `BaseTest` for the actual object `Test`.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
val test = Test::class.resolve()
|
||||
.firstConstructor {
|
||||
emptyParameters()
|
||||
}.createAsType<BaseTest>() // Create a new BaseTest instance.
|
||||
```
|
||||
|
||||
::: tip
|
||||
|
||||
In addition to methods such as `firstMethod`, you can also use methods such as `lastMethod` to get the last matching `MethodResolver` instance, which is equivalent to `method { ... }.last()`.
|
||||
|
||||
After you get the `MemberResolver` instance, you can use `self` to get the `Member` original instance of the current `MemberResolver` to do some of your own operations.
|
||||
|
||||
In `MemberResolver` inherited from `InstanceAwareResolver` (for example `MethodResolver` and `FieldResolver`), you can use `of(instance)`
|
||||
To set the current instance, if the reflection is static (static) member, you do not need to set the instance.
|
||||
|
||||
:::
|
||||
|
||||
::: danger
|
||||
|
||||
In `MemberResolver` inherited from `InstanceAwareResolver`, the type of `of(instance)` requires the same type as the currently reflected `Class` instance generic type.
|
||||
Unless the `Class` generic type is not specified, or the `Class` generic type is set to `Any`.
|
||||
|
||||
If `of(instance)` appears `Required: Nothing?` error (this is usually due to `Class` created via `Class.forName(...)` or `ClassLoader.loadClass(...)`),
|
||||
then your `Class` is `Class<*>` (in Java it is `Class<?>`).
|
||||
At this time, if you do not want to specify the type, please set or convert it to `Class<Any>`, just like the following.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
val myClass = Class.forName("com.xxx.MyClass") as Class<Any>
|
||||
// Suppose this is an example of this Class.
|
||||
val myClassInstance: Any
|
||||
myClass.resolve()
|
||||
.firstMethod {
|
||||
// ...
|
||||
}.of(myClassInstance).invoke(...)
|
||||
```
|
||||
|
||||
You can also use [Create Class Object](kavaref-extension.md#create-class-object) provided in [kavaref-extension](kavaref-extension.md) to solve this problem.
|
||||
|
||||
:::
|
||||
|
||||
### Vague Conditions
|
||||
|
||||
You will notice that there is a `release` method in `Test`, but its method parameters are very long and some types may not be directly obtained.
|
||||
|
||||
At this point, you can use the `parameters(...)` condition to use `VagueType` to fill in the method parameter types you don't want to fill in.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Suppose this is an example of this Class.
|
||||
val test: Test
|
||||
// Call and execute with KavaRef.
|
||||
test.resolve()
|
||||
.firstMethod {
|
||||
name = "release"
|
||||
// Use VagueType to fill in the types you don't want to fill in,
|
||||
// and ensure that other types can match.
|
||||
parameters(String::class, VagueType, Boolean::class)
|
||||
} // Get this method.
|
||||
```
|
||||
|
||||
::: warning
|
||||
|
||||
`VagueType` can only be used when there are filter conditions with multiple parameters,
|
||||
it cannot be used in filter conditions with only a single parameter, such as `type`.
|
||||
|
||||
You can create it using `VagueType`, `VagueType::class` or `VagueType::class.java`, all of which are correctly recognized as fuzzy filtering conditions.
|
||||
|
||||
:::
|
||||
|
||||
### Freedom Conditions
|
||||
|
||||
In `MemberCondition`, `name`, `type`, `parameterCount` and other conditions can all use the Kotlin lambda feature to create free filtering conditions.
|
||||
|
||||
Suppose we want to get the `doTask` method in `Test`, we can use the following implementation.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Suppose this is an example of this Class.
|
||||
val test: Test
|
||||
// Call and execute with KavaRef.
|
||||
test.resolve()
|
||||
.firstMethod {
|
||||
// Use lambda to set the method name.
|
||||
name {
|
||||
// Set the name not case sensitive.
|
||||
it.equals("dotask", ignoreCase = true)
|
||||
}
|
||||
// Set parameter type.
|
||||
parameters(String::class)
|
||||
}.invoke("task_name")
|
||||
```
|
||||
|
||||
### Generic Conditions
|
||||
|
||||
KavaRef supports adding generic filtering conditions, which you can use the relevant functions provided by `TypeMatcher`.
|
||||
|
||||
Suppose we need to filter the `print` method in `Box<String>`.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Suppose this is an example of this Class.
|
||||
val box: Box<String>
|
||||
// Call and execute with KavaRef.
|
||||
box.resolve()
|
||||
.firstMethod {
|
||||
name = "print"
|
||||
// Set generic parameter conditions.
|
||||
genericParametes(
|
||||
// Filter generic name "T".
|
||||
typeVar("T"),
|
||||
// Create TypeMatcher through Class.
|
||||
String::class.toTypeMatcher()
|
||||
)
|
||||
}.invoke("item", "str")
|
||||
```
|
||||
|
||||
### Filter in Superclass
|
||||
|
||||
You will notice that `Test` inherits from `BaseTest`, and now we want to get the `doBaseTask` method of `BaseTest`.
|
||||
|
||||
Without knowing the superclass name, we only need to add `superclass()` to the filter condition to achieve this function.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Suppose this is an example of this Class.
|
||||
val test: Test
|
||||
// Call and execute with KavaRef.
|
||||
test.resolve()
|
||||
.firstMethod {
|
||||
name = "doBaseTask"
|
||||
parameters(String::class)
|
||||
// Just add this condition.
|
||||
superclass()
|
||||
}.invoke("task_name")
|
||||
```
|
||||
|
||||
At this time, we can get this method in the superclass.
|
||||
|
||||
::: tip
|
||||
|
||||
`superclass()` once set it,it will automatically loop backwards whether there is this method in all inherited
|
||||
superclasses until the target has no superclass (the inheritance relationship is `java.lang.Object`).
|
||||
|
||||
:::
|
||||
|
||||
::: danger
|
||||
|
||||
The current filtering method can only filter to the current `Class` method unless the `superclass()` condition is specified,
|
||||
which is the default behavior of the Java reflection API.
|
||||
KavaRef will call `Class.getDeclaredMethods()` to get the current `Class` method instead of `Class.getMethods()`.
|
||||
|
||||
:::
|
||||
|
||||
### Other Conditions
|
||||
|
||||
KavaRef provides some filtering conditions to assist in the use of the Java reflection API.
|
||||
|
||||
Suppose we want to get the contents of the static variable `TAG` in `Test`.
|
||||
|
||||
In order to reflect that the filtering conditions include static descriptors, we can implement them using the following methods.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
val tag = Test::class.resolve()
|
||||
.firstField {
|
||||
name = "TAG"
|
||||
type = String::class
|
||||
// Create descriptor filtering.
|
||||
modifiers(Modifiers.STATIC)
|
||||
// Or.
|
||||
modifiers {
|
||||
it.contains(Modifiers.STATIC)
|
||||
}
|
||||
}.get<String>() // Get field content.
|
||||
```
|
||||
|
||||
You can also use string types to pass in full class names in conditions such as `type`, `parameters`, etc.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Suppose this is an example of this Class.
|
||||
val test: Test
|
||||
// Call and execute with KavaRef.
|
||||
test.resolve()
|
||||
.firstMethod {
|
||||
name = "doTask"
|
||||
// Pass the full class name using string type.
|
||||
parameters("java.lang.String")
|
||||
}.invoke("task_name")
|
||||
```
|
||||
|
||||
### Exception Handling
|
||||
|
||||
By default, KavaRef throws an exception when a member is not found during a reflection call.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
Test::class.resolve()
|
||||
.method {
|
||||
name = "doNonExistentMethod"
|
||||
} // NoSuchMethodException will be thrown here.
|
||||
```
|
||||
|
||||
If you do not want an exception to be thrown, you can set the optional condition `optional()`.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
Test::class.resolve()
|
||||
// Set optional conditions.
|
||||
.optional()
|
||||
.method {
|
||||
name = "doNonExistentMethod"
|
||||
|
||||
} // Return empty List<MethodResolver>.
|
||||
```
|
||||
|
||||
KavaRef prints the complete exception content for debugging, and when using `optional()`, the exception is printed as a WARN level log.
|
||||
|
||||
> The following example
|
||||
|
||||
``` :no-line-numbers
|
||||
No method found matching the condition for current class.
|
||||
+------------------------------------------------+
|
||||
| class com.demo |
|
||||
+------------+-----------------------------------+
|
||||
| name | doNonExistentMethod |
|
||||
| parameters | [class java.lang.String, boolean] |
|
||||
+------------+-----------------------------------+
|
||||
```
|
||||
|
||||
If you don't want KavaRef to throw or print anything, you can use `optional(silent = true)` to silently handle it,
|
||||
but we **do not recommend this**, which will mask the problem unless it is necessary.
|
||||
|
||||
::: danger
|
||||
|
||||
If you set `optional()`, please do not use `firstMethod`, `firstConstructor` and other methods to get a single result.
|
||||
Because they throw an exception with empty list when there is no result, you can use the method with the suffix `OrNull` to get a single result.
|
||||
|
||||
:::
|
||||
|
||||
### Log Management
|
||||
|
||||
KavaRef provides its own log management function, you can set the log level through `KavaRef.logLevel`.
|
||||
|
||||
You can set `KavaRef.logLevel = KavaRefRuntime.LogLevel.DEBUG` to enable DEBUG level logs so that KavaRef
|
||||
prints more detailed step-by-step filtering condition logs to the console during the filtering process.
|
||||
|
||||
If you want to turn off all log printing of KavaRef, you can set `KavaRef.logLevel = KavaRefRuntime.LogLevel.OFF`.
|
||||
|
||||
If you have more advanced requirements, you can implement `KavaRefRuntime.Logger` to customize your own log printing method.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
class MyLogger : KavaRefRuntime.Logger {
|
||||
|
||||
// Here you can specify the tag for log printing.
|
||||
override val tag = "MyLogger"
|
||||
|
||||
override fun debug(msg: Any?, throwable: Throwable?) {
|
||||
// Implement your log printing logic here.
|
||||
}
|
||||
|
||||
override fun info(msg: Any?, throwable: Throwable?) {
|
||||
// Implement your log printing logic here.
|
||||
}
|
||||
|
||||
override fun warn(msg: Any?, throwable: Throwable?) {
|
||||
// Implement your log printing logic here.
|
||||
}
|
||||
|
||||
override fun error(msg: Any?, throwable: Throwable?) {
|
||||
// Implement your log printing logic here.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then, set it to KavaRef.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
KavaRef.setLogger(MyLogger())
|
||||
```
|
||||
|
||||
### Advanced Usage
|
||||
|
||||
The above content explains all the usage methods in standard scenarios.
|
||||
If you have a more fine-grained usage scenario, you can manually create related components of KavaRef.
|
||||
|
||||
If you don't like the Kotlin lambda writing, you can create chained calls manually.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Suppose this is an example of this Class.
|
||||
val test: Test
|
||||
// Call and execute with KavaRef.
|
||||
test.resolve()
|
||||
.method() // Conditions begin.
|
||||
.name("doTask")
|
||||
.parameters(String::class)
|
||||
.build() // Conditions ends (executes)
|
||||
.first()
|
||||
.invoke("task_name")
|
||||
```
|
||||
|
||||
You can also manually create any filtering conditions to achieve multiplexing it in any reflection.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Suppose this is an example of this Class.
|
||||
val test: Test
|
||||
// Create MethodCondition manually.
|
||||
val condition = MethodCondition<Test>()
|
||||
condition.name = "doTask"
|
||||
condition.parameters(String::class)
|
||||
// Apply condition to reflection object.
|
||||
Test::class.resolve()
|
||||
.firstMethod(condition)
|
||||
.of(test) // Setting up instance.
|
||||
.invoke("task_name")
|
||||
```
|
||||
|
||||
Alternatively, you can also manually and completely implement the entire reflection process.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Suppose this is an example of this Class.
|
||||
val test: Test
|
||||
// Create MethodCondition manually.
|
||||
val condition = MethodCondition<Test>()
|
||||
condition.name = "doTask"
|
||||
condition.parameters(String::class)
|
||||
// Create MemberCondition.Configuration manually.
|
||||
val configuration = Test::class.java.createConfiguration(
|
||||
memberInstance = test, // Setting up instance.
|
||||
processorResolver = null, // Use the default resolver, refer to the "Custom Resolver" below.
|
||||
superclass = false, // Whether to filter in superclass.
|
||||
optional = MemberCondition.Configuration.Optional.NO // Configure optional conditions.
|
||||
)
|
||||
// Create and start filtering.
|
||||
val resolvers = condition.build(configuration)
|
||||
// Get the first result.
|
||||
val resolver = resolvers.first()
|
||||
// Execute the method.
|
||||
resolver.invoke("task_name")
|
||||
```
|
||||
|
||||
If you have more advanced requirements for business layer logic, you can also use `mergeWith` to merge multiple filter conditions.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Suppose this is an instance of this Class.
|
||||
val test: Test
|
||||
// Create MethodCondition manually.
|
||||
// Create the first condition.
|
||||
val condition1 = MethodCondition<Test>()
|
||||
condition1.name = "doTask"
|
||||
// Create a second condition.
|
||||
val condition2 = MethodCondition<Test>()
|
||||
condition2.parameters(String::class)
|
||||
// Merge condition2 into condition1.
|
||||
// At this time, the condition of condition1 will contain the condition that condition2 is not null.
|
||||
// The duplicated conditions in condition1 will be overwritten by the condition2 condition.
|
||||
condition1.mergeWith(condition2)
|
||||
// You can also use the infix syntax.
|
||||
condition1 mergeWith condition2
|
||||
// Call and execute with KavaRef.
|
||||
Test::class.resolve()
|
||||
.firstMethod(condition1)
|
||||
.of(test)
|
||||
.invoke("task_name")
|
||||
```
|
||||
|
||||
::: danger
|
||||
|
||||
Reused use of `build(...)` will no longer be allowed for creation when `MemberCondition.Configuration` is set.
|
||||
At this point you need to use `copy()` to copy and create a new `MemberCondition`.
|
||||
|
||||
Similarly, `InstanceAwareResolver` is not allowed to duplicately set up new instances after setting the instance via `MemberCondition.Configuration.memberInstance` or `of(instance)`.
|
||||
At this time, you also need to use `copy()` to copy and create a new `InstanceAwareResolver`.
|
||||
|
||||
:::
|
||||
|
||||
### Custom Resolver
|
||||
|
||||
KavaRef uses the default `Member` resolver for filtering.
|
||||
If you want to implement your own resolver, you can customize the global and the resolver used for each reflection process.
|
||||
|
||||
You can inherit from `MemberProccessor.Resolver` to implement your own resolver.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
class MyMemberProcessorResolver : MemberProcessor.Resolver() {
|
||||
|
||||
override fun <T : Any> getDeclaredConstructors(declaringClass: Class<T>): List<Constructor<T>> {
|
||||
// Intercept and implement your constructor filtering logic here.
|
||||
return super.getDeclaredConstructors(declaringClass)
|
||||
}
|
||||
|
||||
override fun <T : Any> getDeclaredMethods(declaringClass: Class<T>): List<Method> {
|
||||
// Intercept and implement your method filtering logic here.
|
||||
return super.getDeclaredMethods(declaringClass)
|
||||
}
|
||||
|
||||
override fun <T : Any> getDeclaredFields(declaringClass: Class<T>): List<Field> {
|
||||
// Intercept and implement your field filtering logic here.
|
||||
return super.getDeclaredFields(declaringClass)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can then set it to the global configuration.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
MemberProcessor.globalResolver = MyMemberProcessorResolver()
|
||||
```
|
||||
|
||||
Alternatively, during each reflection, you can set up a custom resolver using `MemberCondition.Configuration` or use a chain call to set up a resolver.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Create resolver.
|
||||
val myResolver = MyMemberProcessorResolver()
|
||||
// Suppose this is an instance of this Class.
|
||||
val test: Test
|
||||
// Call and execute using KavaRef.
|
||||
test.resolve()
|
||||
// Set custom resolver.
|
||||
.processor(myResolver)
|
||||
.firstMethod {
|
||||
name = "doTask"
|
||||
parameters(String::class)
|
||||
}.invoke("task_name")
|
||||
```
|
||||
|
||||
::: tip
|
||||
|
||||
You can find some publicly maintained custom solvers in [here](../config/processor-resolvers.md) and define them in your project to use.
|
||||
|
||||
:::
|
||||
|
||||
### About Cache
|
||||
|
||||
Due to the diversity of filtering conditions, KavaRef does not directly provide caching function,
|
||||
and the implementation method of caching will also vary depending on the implementation method of each developer.
|
||||
|
||||
We recommend manually implementing caches of `MemberResolver` created with filter results for improved performance
|
||||
and refer to [Create Manually](#create-manually) to split filter conditions to optimize code reuse.
|
||||
|
||||
::: danger
|
||||
|
||||
If you use `val myResolver by lazy { ... }` to implement the cache, for example, do so below.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
val myResolver by lazy {
|
||||
Test::class.resolve()
|
||||
.firstMethod {
|
||||
name = "doTask"
|
||||
parameters(String::class)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You may do this when calling.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Suppose this is an instance of this Class.
|
||||
val test: Test
|
||||
// Call and execute using KavaRef.
|
||||
myResolver.of(test).invoke("task_name")
|
||||
```
|
||||
|
||||
Please note that since `MemberResolver` is cached, the same instance is called every time you reference it,
|
||||
and the instance object of `MemberResolver` is not allowed to be set duplicately (see the "Pay Attention" below [Create Manually](#create-manually)).
|
||||
|
||||
So calling this directly will throw an exception, you need to change it to the following form.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Suppose this is an instance of this Class.
|
||||
val test: Test
|
||||
// Call and execute using KavaRef.
|
||||
myResolver.copy().of(test).invoke("task_name")
|
||||
```
|
||||
|
||||
This allows you to copy a new `MemberResolver` instance every time you call without repeating the reflection process and throwing an exception.
|
||||
|
||||
:::
|
||||
|
||||
### Java Usage
|
||||
|
||||
KavaRef is not recommended to be used directly in Java because its API design is based on Kotlin's features and syntax sugar.
|
||||
|
||||
If you need to use KavaRef in Java, you can do it in the following ways.
|
||||
|
||||
> The following example
|
||||
|
||||
```java
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Suppose this is an example of this Class.
|
||||
Test test;
|
||||
// Call and execute with KavaRef.
|
||||
KavaRef.resolveClass(Test.class)
|
||||
.method()
|
||||
.name("doTask")
|
||||
.parameters(String.class)
|
||||
.build()
|
||||
.get(0)
|
||||
.of(test)
|
||||
.invoke("task_name");
|
||||
// Or create KavaRef reflections using an instance.
|
||||
KavaRef.resolveObject(test)
|
||||
.method()
|
||||
.name("doTask")
|
||||
.parameters(String.class)
|
||||
.build()
|
||||
.get(0)
|
||||
.invoke("task_name");
|
||||
}
|
||||
}
|
||||
```
|
315
docs-source/src/en/library/kavaref-extension.md
Normal file
315
docs-source/src/en/library/kavaref-extension.md
Normal file
@@ -0,0 +1,315 @@
|
||||
# kavaref-extension
|
||||
|
||||

|
||||
<span style="margin-left: 5px"/>
|
||||

|
||||
|
||||
This is an extended dependency for KavaRef-related features.
|
||||
|
||||
## Configure Dependency
|
||||
|
||||
You can add this module to your project using the following method.
|
||||
|
||||
### SweetDependency (Recommended)
|
||||
|
||||
Add dependency in your project's `SweetDependency` configuration file.
|
||||
|
||||
```yaml
|
||||
libraries:
|
||||
com.highcapable.kavaref:
|
||||
kavaref-extension:
|
||||
version: +
|
||||
```
|
||||
|
||||
Configure dependency in your project `build.gradle.kts`.
|
||||
|
||||
```kotlin
|
||||
implementation(com.highcapable.kavaref.kavaref.extension)
|
||||
```
|
||||
|
||||
### Version Catalog
|
||||
|
||||
Add dependency in your project's `gradle/libs.versions.toml`.
|
||||
|
||||
```toml
|
||||
[versions]
|
||||
kavaref-extension = "<version>"
|
||||
|
||||
[libraries]
|
||||
kavaref-extension = { module = "com.highcapable.kavaref:kavaref-extension", version.ref = "kavaref-extension" }
|
||||
```
|
||||
|
||||
Configure dependency in your project `build.gradle.kts`.
|
||||
|
||||
```kotlin
|
||||
implementation(libs.kavaref.extension)
|
||||
```
|
||||
|
||||
Please change `<version>` to the version displayed at the top of this document.
|
||||
|
||||
### Traditional Method
|
||||
|
||||
Configure dependency in your project `build.gradle.kts`.
|
||||
|
||||
```kotlin
|
||||
implementation("com.highcapable.kavaref:kavaref-extension:<version>")
|
||||
```
|
||||
|
||||
Please change `<version>` to the version displayed at the top of this document.
|
||||
|
||||
## Function Introduction
|
||||
|
||||
You can view the KDoc [click here](kdoc://kavaref-extension).
|
||||
|
||||
### Class Extensions
|
||||
|
||||
KavaRef provides some extensions that are more convenient when dealing with `Class` objects.
|
||||
|
||||
KavaRef also adds the `KClass` extensions to the `Class` extensions,
|
||||
which is used to call `KClass.java`, which is more concise than using `Some::class.java` directly.
|
||||
|
||||
#### Create Class Object
|
||||
|
||||
For example, we need to create a `Class` object using the string class name.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
val myClass = "com.example.MyClass".toClass()
|
||||
// You can use a method with OrNull suffix to return null
|
||||
// when Class is not found instead of throwing an exception.
|
||||
val myClassOrNull = "com.example.MyClass".toClassOrNull()
|
||||
```
|
||||
|
||||
These methods use `ClassLoaderProvider` to get the default `ClassLoader`,
|
||||
you can set the default `ClassLoader` to affect global functionality.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
ClassLoaderProvider.classLoader = MyCustomClassLoader()
|
||||
```
|
||||
|
||||
You can also manually pass a `ClassLoader` parameter to the `toClass` method to specify which `ClassLoader` to use.
|
||||
|
||||
#### Class Object Reference
|
||||
|
||||
Referring to Java Class in Kotlin requires writing a very long statement,
|
||||
such as `MyClass::class.java`, which you can simplify in the following ways.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
val myClass = classOf<MyClass>()
|
||||
```
|
||||
|
||||
You can use the `isSubclassOf` method to determine whether a `Class` is another `Class` subclass.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
val isSubclass = MyClass::class isSubclassOf MySuperClass::class
|
||||
// Of course, it also has a corresponding antonym of judgment.
|
||||
val isNotSubclass = MyClass::class isNotSubclassOf MySuperClass::class
|
||||
```
|
||||
|
||||
You can also use the `hasSuperclass` and `hasInterfaces` methods to determine whether a `Class` has a superclass or an interface.
|
||||
|
||||
::: danger
|
||||
|
||||
The `Class` passed in by the `classOf` method will perform unboxing of Java wrapper classes by default,
|
||||
whether you pass in something like `kotlin.Boolean` or `java.lang.Boolean` (see [Java Wrapper Classes Extensions](#java-wrapper-classes-extensions) below),
|
||||
If you need to avoid the incoming `Class` being unboxed into primitive types, you need to explicitly set the `primitiveType = false` parameter.
|
||||
|
||||
:::
|
||||
|
||||
#### Create New Instance
|
||||
|
||||
KavaRef provides a way for `Class` to easily create a new instance.
|
||||
You don't need to consider the type of constructing parameters,
|
||||
you just need to pass in the corresponding parameters to create a new instance immediately.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
val myClass = MyClass::class.createInstance("Hello", 123)
|
||||
// You can also use a method with the OrNull suffix to return null
|
||||
// when creation fails instead of throwing an exception.
|
||||
val myClassOrNull = MyClass::class.createInstanceOrNull("Hello", 123)
|
||||
// The createInstance method only filters public constructors by default.
|
||||
// If you need to call non-public constructors, please set isPublic = false.
|
||||
val myClassWithPrivateConstructor = MyClass::class.createInstance("Private!", isPublic = false)
|
||||
// If you want to specify the type to create an instance to use another type,
|
||||
// you can use the following method.
|
||||
val mySuperClass = MyClass::class.createInstanceAsType<MySuperClass>("Hello", 123)
|
||||
// Similarly, you can use a method with the OrNull suffix to return null when
|
||||
// creation fails instead of throwing an exception.
|
||||
val mySuperClassOrNull = MyClass::class.createInstanceAsTypeOrNull<MySuperClass>("Hello", 123)
|
||||
```
|
||||
|
||||
::: tip
|
||||
|
||||
After the `createInstance` method is successfully matched once, it will cache the results to prevent performance losses
|
||||
caused by duplicated reflections. It is thread-safe and you can use it in any standard scenario with confidence.
|
||||
|
||||
:::
|
||||
|
||||
::: danger
|
||||
|
||||
When you pass in a parameter with `null`, KavaRef tries to use it as part of the matchable condition (vague condition), and the accuracy may decrease.
|
||||
|
||||
The `createInstance` method does not allow all parameters to be `null` (the conditions are completely vague),
|
||||
and an exception will be thrown directly because this situation cannot be determined which instance to create.
|
||||
|
||||
:::
|
||||
|
||||
#### Class Modifier
|
||||
|
||||
KavaRef also extends `Modifier`, you can directly use `Class.isPublic` and other methods to judge a `Class` modifier.
|
||||
|
||||
#### VariousClass
|
||||
|
||||
KavaRef provides the `VariousClass` class to load the `Class` object with an indeterminate full class name and return the first match successfully.
|
||||
|
||||
This feature is usually used for class names in Android apps that are obfuscated by R8.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Assume that in version A, this class is com.example.a,
|
||||
// In version B, this class is com.example.b.
|
||||
val myClass = VariousClass("com.example.a", "com.example.b").load()
|
||||
// You can also use a method with the suffix OrNull to return null
|
||||
// instead of throwing an exception if Class is not found.
|
||||
val myClassOrNull = VariousClass("com.example.a", "com.example.b").loadOrNull()
|
||||
```
|
||||
|
||||
#### Lazy Loading Class Object
|
||||
|
||||
KavaRef provides the `LazyClass` class to lazy loading the `Class` object.
|
||||
|
||||
You can load `Class` when needed, instead of loading it immediately when created,
|
||||
which can solve some `Class` that need to be loaded when run or run to specific conditions.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Define a Class that cannot be loaded for null and hosts it to myClass.
|
||||
val myClass by lazyClass("com.example.MyClass")
|
||||
// Define a Class that can be loaded for null delay and host it to myClassOrNull.
|
||||
val myClassOrNull by lazyClassOrNull("com.example.MyClass")
|
||||
// It can also support incoming VariousClass.
|
||||
val otherClassOrNull by lazyClassOrNull(VariousClass("com.example.a", "com.example.b"))
|
||||
// Called and loaded when needed.
|
||||
myClass.resolve()
|
||||
myClassOrNull?.resolve()
|
||||
otherClassOrNull?.resolve()
|
||||
```
|
||||
|
||||
#### ClassLoader Extensions
|
||||
|
||||
KavaRef also provides some practical extension methods for `ClassLoader`.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Assume that's your ClassLoader.
|
||||
val classLoader: ClassLoader
|
||||
// Load a Class and return null if the load fails.
|
||||
val myClassOrNull = classLoader.loadClassOrNull("com.example.MyClass")
|
||||
// Determine whether this Class exists in the current ClassLoader.
|
||||
val isClassExists = classLoader.hasClass("com.example.MyClass")
|
||||
```
|
||||
|
||||
### Array Class Extensions
|
||||
|
||||
In Java, the `Class` object of an array is a special `Class` object, and usually we create it as follows.
|
||||
|
||||
For example, create a `Class` object of `java.lang.String[]`.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
val arrayClass = java.lang.reflect.Array.newInstance(String::class.java, 0).javaClass
|
||||
```
|
||||
|
||||
This is very long to write and is not convenient to maintain, so KavaRef provides a way to simplify this process.
|
||||
|
||||
Now, the `Class` object that creates `java.lang.String[]` can be written like this.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
val arrayClass = ArrayClass(String::class)
|
||||
```
|
||||
|
||||
### Member Extensions
|
||||
|
||||
KavaRef provides some extension methods to simplify operations on `Member`.
|
||||
|
||||
You can set its accessibility using the `makeAccessible` method on any `Member` object.
|
||||
|
||||
It will take effect if `Member` is the `AccessibleObject` type.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Suppose this is your current Member object.
|
||||
val method: Method
|
||||
// Make method is accessible.
|
||||
method.makeAccessible()
|
||||
```
|
||||
|
||||
Similarly, KavaRef also extends `Modifier`, and you can directly use `Member.isPublic` and other methods to judge a `Member` modifier.
|
||||
|
||||
### Type Extensions
|
||||
|
||||
When manipulating types or generic types in Java, you usually need to use the `Type` interface and its subinterface to handle it.
|
||||
|
||||
KavaRef provides some extension methods to simplify operations on `Type`.
|
||||
|
||||
For example, you can convert a `Type` that meets the requirements to a `Class` object.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
val type: Type
|
||||
val clazz = type.toClass()
|
||||
// You can also use a method with the suffix named OrNull to
|
||||
// return null when the conversion fails instead of throwing an exception.
|
||||
val clazzOrNull = type.toClassOrNull()
|
||||
```
|
||||
|
||||
You can also convert `Type` that meets the requirements to `ParameterizedType` object.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
val type: Type
|
||||
val parameterizedType = type.asParameterizedType()
|
||||
// You can also use a method with the suffix named OrNull to
|
||||
// return null when the conversion fails instead of throwing an exception.
|
||||
val parameterizedTypeOrNull = type.asParameterizedTypeOrNull()
|
||||
```
|
||||
|
||||
You can also use the following method to get the generic parameter array in the superclass,
|
||||
which is often used in some superclass and subclass encapsulation operations.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
val myClass: Class<*>
|
||||
// Get the generic parameter array of myClass superclass.
|
||||
// If the acquisition fails or cannot be retrieved, the empty array will be returned.
|
||||
val arguments = myClass.genericSuperclassTypeArguments()
|
||||
```
|
||||
|
||||
### Java Wrapper Classes Extensions
|
||||
|
||||
In Kotlin, you can directly use `Boolean::class`, `Byte::class`, etc. to obtain Java's original types `boolean` and `byte` instead of their wrapper classes.
|
||||
|
||||
If you need to get Java wrapper classes, you need to use the complete `java.lang.Boolean::class`, `java.lang.Byte::class`, etc.
|
||||
or use `Boolean::class.javaObjectType`, `Byte::class.javaObjectType`.
|
||||
|
||||
So, KavaRef provides some type alias to handle Java wrapper classes. Now you only need to prefix `J` to these types, such as `JBoolean::class`.
|
||||
It is equivalent to `java.lang.Boolean::class`, and some types need to be filled in the full name, such as `JInteger::class`.
|
17
docs-source/src/index.md
Normal file
17
docs-source/src/index.md
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
home: true
|
||||
navbar: false
|
||||
sidebar: false
|
||||
title: null
|
||||
heroAlt: null
|
||||
heroText: null
|
||||
tagline: Select a language
|
||||
actions:
|
||||
- text: English
|
||||
link: /en/
|
||||
type: secondary
|
||||
- text: 简体中文
|
||||
link: /zh-cn/
|
||||
type: secondary
|
||||
footer: Apache-2.0 License | Copyright (C) 2019 HighCapable
|
||||
---
|
27
docs-source/src/zh-cn/about/about.md
Normal file
27
docs-source/src/zh-cn/about/about.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# 关于此文档
|
||||
|
||||
> 此文档由 [VuePress](https://v2.vuepress.vuejs.org/zh) 强力驱动。
|
||||
|
||||
## 许可证
|
||||
|
||||
[Apache-2.0](https://github.com/HighCapable/KavaRef/blob/main/LICENSE)
|
||||
|
||||
```:no-line-numbers
|
||||
Apache License Version 2.0
|
||||
|
||||
Copyright (C) 2019 HighCapable
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
```
|
||||
|
||||
版权所有 © 2019 HighCapable
|
21
docs-source/src/zh-cn/about/changelog.md
Normal file
21
docs-source/src/zh-cn/about/changelog.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# 更新日志
|
||||
|
||||
> 这里记录了 `KavaRef` 的版本更新历史。
|
||||
|
||||
::: danger
|
||||
|
||||
我们只会对最新的 API 版本进行维护,若你正在使用过时的 API 版本则代表你自愿放弃一切维护的可能性。
|
||||
|
||||
:::
|
||||
|
||||
## kavaref-core
|
||||
|
||||
### 1.0.0 | 2025.06.25  <Badge type="tip" text="最新" vertical="middle" />
|
||||
|
||||
- 首个版本提交至 Maven
|
||||
|
||||
## kavaref-extension
|
||||
|
||||
### 1.0.0 | 2025.06.25  <Badge type="tip" text="最新" vertical="middle" />
|
||||
|
||||
- 首个版本提交至 Maven
|
15
docs-source/src/zh-cn/about/contacts.md
Normal file
15
docs-source/src/zh-cn/about/contacts.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# 联系我们
|
||||
|
||||
> 如在使用中有任何问题,或有任何建设性的建议,都可以联系我们。
|
||||
|
||||
加入我们的开发者群组。
|
||||
|
||||
- [点击加入 Telegram 群组](https://t.me/KavaRef)
|
||||
- [点击加入 Telegram 群组 (开发者)](https://t.me/HighCapable_Dev)
|
||||
- [点击加入 QQ 群 (开发者)](https://qm.qq.com/cgi-bin/qm/qr?k=Pnsc5RY6N2mBKFjOLPiYldbAbprAU3V7&jump_from=webapi&authKey=X5EsOVzLXt1dRunge8ryTxDRrh9/IiW1Pua75eDLh9RE3KXE+bwXIYF5cWri/9lf)
|
||||
|
||||
在 **酷安** 找到我 [@星夜不荟](http://www.coolapk.com/u/876977)。
|
||||
|
||||
## 助力维护
|
||||
|
||||
感谢您选择并使用 `KavaRef`,如有代码相关的建议和请求,可在 GitHub 提交 Pull Request。
|
113
docs-source/src/zh-cn/about/future.md
Normal file
113
docs-source/src/zh-cn/about/future.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# 展望未来
|
||||
|
||||
> 未来是美好的,也是不确定的,让我们共同期待 `KavaRef` 在未来的发展空间。
|
||||
|
||||
## 未来的计划
|
||||
|
||||
> 这里收录了 `KavaRef` 可能会在后期添加的功能。
|
||||
|
||||
### 支持通过 ClassLoader 过滤 Class
|
||||
|
||||
`KavaRef` 目前仅支持 `Method`、`Field`、`Constructor` 等反射 API 的查找和调用,
|
||||
未来可能会根据需求在 Java 与 Android 平台支持通过指定类型的 `ClassLoader` 过滤 `Class` 的功能。
|
||||
|
||||
目前,你可以使用 [DexKit](https://github.com/LuckyPray/DexKit) 来完成这一需求,它同时支持更加复杂的 Method、Field、Constructor 等反射 API 的查找和调用。
|
||||
|
||||
### 自动生成反射代码
|
||||
|
||||
**这是在 [YukiReflection](https://github.com/HighCapable/YukiReflection) 中已经初步立项的功能,`KavaRef` 准备在未来可能的时间里继续实现它。**
|
||||
|
||||
使用 `stub` 的方式创建一个 Kotlin 类,并声明其中的参数,以及其在各个版本中的不同状态。
|
||||
|
||||
比如下面的这个 Java 类就是我们需要反射的目标类。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```java:no-line-numbers
|
||||
package com.example.test;
|
||||
|
||||
public class MyClass {
|
||||
|
||||
private String myField = "test";
|
||||
|
||||
public MyClass() {
|
||||
// ...
|
||||
}
|
||||
|
||||
private String myMethod1(String var1, int var2) {
|
||||
// ...
|
||||
}
|
||||
|
||||
private void myMethod2() {
|
||||
// ...
|
||||
}
|
||||
|
||||
private void myMethod3(String var1) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
通过目前 API 的现有用法可以使用如下方式反射调用这个类。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
MyClass().resolve().apply {
|
||||
// 调用 myField
|
||||
val value = firstField { name = "myField" }.get<String>()
|
||||
// 调用 myMethod1
|
||||
val methodValue = firstMethod { name = "myMethod1" }.invoke<String>("test", 0)
|
||||
// 调用 myMethod2
|
||||
firstMethod { name = "myMethod2" }.invoke()
|
||||
// 调用 myMethod3
|
||||
firstMethod { name = "myMethod3" }.invoke("test")
|
||||
}
|
||||
```
|
||||
|
||||
目前要实现的功能是可以使用反射功能直接定义为如下 Kotlin 类。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
package com.example.test
|
||||
|
||||
@ReflectClass
|
||||
class MyClass {
|
||||
|
||||
@ReflectField
|
||||
val myField: String = fieldValueOf("none")
|
||||
|
||||
@ReflectMethod
|
||||
fun myMethod1(var1: String, var2: Int): String = methodReturnValueOf("none")
|
||||
|
||||
@ReflectMethod
|
||||
fun myMethod2() = MethodReturnType.Unit
|
||||
|
||||
@ReflectMethod
|
||||
fun myMethod3(var1: String) = MethodReturnType.Unit
|
||||
}
|
||||
```
|
||||
|
||||
然后我们就可以直接调用这个定义好的 Kotlin 类来实现反射功能,API 会根据注解自动生成反射代码。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
MyClass().also {
|
||||
// 调用 myField
|
||||
val value = it.myField
|
||||
// 调用 myMethod1
|
||||
val methodValue = it.myMethod1("test", 0)
|
||||
// 调用 myMethod2
|
||||
it.myMethod2()
|
||||
// 调用 myMethod3
|
||||
it.myMethod3("test")
|
||||
}
|
||||
```
|
||||
|
||||
::: tip
|
||||
|
||||
以上功能可能会在实际推出后有所变化,最终以实际版本的功能为准。
|
||||
|
||||
:::
|
243
docs-source/src/zh-cn/config/migration.md
Normal file
243
docs-source/src/zh-cn/config/migration.md
Normal file
@@ -0,0 +1,243 @@
|
||||
# 迁移至 KavaRef
|
||||
|
||||
如果你已经习惯使用 [YukiReflection](https://github.com/HighCapable/YukiReflection) 或 [YukiHookAPI](https://github.com/HighCapable/YukiHookAPI) 中的反射 API,你可以参考以下内容来迁移至 `KavaRef`。
|
||||
|
||||
::: warning
|
||||
|
||||
针对 `YukiHookAPI`,你需要继续使用其 Hook API,`KavaRef` 仅包含 Java 反射相关 API。
|
||||
|
||||
:::
|
||||
|
||||
## 基本功能
|
||||
|
||||
`KavaRef` 的设计理念与 `YukiReflection` 类似,但不完全相同,以下内容列举了 `YukiReflection` 与 `KavaRef` 在基本反射功能上的差异,你可以根据这些差异手动进行迁移。
|
||||
|
||||
例如我们有以下 Java 类。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```java :no-line-numbers
|
||||
public class MyClass {
|
||||
|
||||
private static String content = "Hello, World!";
|
||||
|
||||
private void myMethod(String content) {
|
||||
System.out.println("Hello " + content + "!");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
以下是 `KavaRef` 与 `YukiReflection` 的使用示例对比。
|
||||
|
||||
> 示例如下
|
||||
|
||||
<div style="display: flex; gap: 16px;">
|
||||
|
||||
<div style="flex: 1;">
|
||||
<h4>KavaRef</h4>
|
||||
|
||||
```kotlin
|
||||
// 假设这就是你的 MyClass 实例
|
||||
val myClass: MyClass
|
||||
// 使用 KavaRef 调用并执行
|
||||
MyClass::class.resolve().firstMethod {
|
||||
name = "myMethod"
|
||||
parameters(String::class)
|
||||
}.of(myClass).invoke("Hello, KavaRef!")
|
||||
// 直接引用实例方式
|
||||
myClass.resolve().firstMethod {
|
||||
name = "myMethod"
|
||||
parameters(String::class)
|
||||
}.invoke("Hello, KavaRef!")
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
<div style="flex: 1;">
|
||||
<h4>YukiReflection</h4>
|
||||
|
||||
```kotlin
|
||||
// 假设这就是你的 MyClass 实例
|
||||
val myClass: MyClass
|
||||
// 使用 YukiReflection 调用并执行
|
||||
MyClass::class.java.method {
|
||||
name = "myMethod"
|
||||
param(StringClass)
|
||||
}.get(myClass).call("Hello, YukiReflection!")
|
||||
// 直接引用实例方式
|
||||
myClass.current().method {
|
||||
name = "myMethod"
|
||||
param(StringClass)
|
||||
}.call("Hello, YukiReflection!")
|
||||
```
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
`KavaRef` 在任何时候开始反射都需要使用 `resolve()` 来创建反射作用域,不再对 `Class` 等实例直接进行扩展相关 `method`、`constructor` 方法以避免污染其作用域。
|
||||
|
||||
`KavaRef` 抛弃了 "Finder" 的设计理念,使用 "Filter" (过滤器) 的设计理念来获取反射结果,“查找” 不再是查找,而是 “过滤”。
|
||||
|
||||
`KavaRef` 取消了 `YukiReflection` 在结果实例中定义获取的 `Member` 为多重还是单一的设计方案,直接返回整个 `List<MemberResolver>`,
|
||||
你在上方看到的示例使用了 `firstMethod` 来获取第一个匹配的 `MethodResolver`,如果你需要获取所有匹配的结果,可以改为 `method`。
|
||||
|
||||
`KavaRef` 在 `MethodCondition` 中的条件方法名称已由 `YukiReflection` 之前的 `param` 等简写修改为 `parameters`,以更符合 Java 反射 API 的命名习惯。
|
||||
|
||||
`KavaRef` 不再提供条件中的 `param(...).order()` 功能,因为这个功能本身就不稳定,`KavaRef` 现在使用迭代器进行过滤操作,字节码将不再有顺序,且本不应该使用顺序筛选字节码,你可以使用 `firstMethod`、`firstField` 或 `lastMethod`、`lastField` 等方法来获取第一个或最后一个匹配的结果。
|
||||
|
||||
`KavaRef` 将 `get(instance)` 方法更名为 `of(instance)`,因为 `get(...)` 可能会与 `Field` 的 `get(...)` 用法产生混淆且语义不明确,
|
||||
同时 `get(instance)` 也不再是从类似 `MethodFinder.Result` 来获取 `MethodFinder.Result.Instance` 实例,而是使用 `of(instance)` 来始终操作和设置实例对象到 `MemberResolver`。
|
||||
|
||||
类似 `MethodFinder.Result.Instance` 中的 `string()`、`int()` 等方法在 `KavaRef` 中已被移除,
|
||||
你可以直接使用 `get<String>()`、`get<Int>()`、`invoke<String>(...)`、`invoke<Int>(...)` 等方式来获取或调用对应类型的结果。
|
||||
|
||||
::: danger
|
||||
|
||||
如果你正在查找 (过滤) `Field`,你需要注意 `KavaRef` 与 `YukiReflection` 在 `Field` 的获取方式上有可能会发生语义冲突,在迁移这部分的时候请特别注意。
|
||||
|
||||
例如获取 `MyClass` 中的 `content` 静态字段,在 `YukiReflection` 中,你会这样做。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
MyClass::class.java
|
||||
.field { name = "content" } // 返回 FieldFinder.Result
|
||||
.get() // 不可省略,返回 FieldFinder.Result.Instance
|
||||
.string() // 值
|
||||
```
|
||||
|
||||
在 `KavaRef` 中,你需要这样做。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
MyClass::class.resolve()
|
||||
.firstField { name = "content" } // 返回 FieldResolver<MyClass>
|
||||
.get<String>() // 值
|
||||
```
|
||||
|
||||
正如上面所说,`get(...)` 在 `YukiReflection` 中是获取 `FieldFinder.Result.Instance` 对象,而不是值,要获取值并处理为指定类型,你需要调用 `string()` 或者 `cast<String>()`,而在 `KavaRef` 中是在 `MemberResolver` 中直接使用 `get<T>()` 来获取指定类型的值,`KavaRef` 对应 `YukiReflection` 的 `get(...)` 的用法是 `of(...)`。
|
||||
|
||||
所以上述示例在 `KavaRef` 中的完整写法应该为。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// 由于调用的是静态实例,"of(null)" 可被省略
|
||||
MyClass::class.resolve()
|
||||
.firstField { name = "content" } // 已是调用链对象 FieldResolver<MyClass>
|
||||
.of(null) // 可省略,返回调用链对象 FieldResolver<MyClass>
|
||||
.get<String>() // 值
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
`KavaRef` 不再对 `Method` 提供 `call` 方法,现在统一合并为 `invoke` (带泛型参数),同时 `KavaRef` 将 `Constructor` 的 `newInstance` 方法定义为 `create` (带泛型参数)。
|
||||
|
||||
你可能注意到条件 `superClass()` 消失了,它还在,在 `KavaRef` 中它已更名为 `superclass()`,对接标准的 Java 反射 API。
|
||||
|
||||
同时,`KavaRef` 对 `KClass` 进行了扩展,你不再需要在大部分场景中使用 `Some::class.java` 的方式来声明一个 `Class` 实例。
|
||||
|
||||
`KavaRef` 的另一个设计思想就是类型安全,只要是你在使用声明指定泛型类型的 `KClass<T>`、`Class<T>` 时,在 `of(instance)`、`create(...)` 时都会校验、转换为对应类型,在编码期间就完成类型检查,避免运行时错误。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// 假设这就是你的 MyClass 实例
|
||||
val myClass: MyClass
|
||||
// 使用 KavaRef 调用并执行
|
||||
MyClass::class
|
||||
.resolve()
|
||||
.firstMethod {
|
||||
name = "myMethod"
|
||||
parameters(String::class)
|
||||
}
|
||||
// 只能传入 MyClass 类型的实例
|
||||
.of(myClass)
|
||||
.invoke("Hello, KavaRef!")
|
||||
```
|
||||
|
||||
## 其它功能
|
||||
|
||||
`KavaRef` 与 `YukiReflection` 在其它功能及扩展功能中的实现差异不大,`KavaRef` 将这些功能单独分离为了一个独立的模块。
|
||||
|
||||
以下功能在 `YukiReflection` 中提供,但在 `KavaRef` 中没有实现且不再提供:
|
||||
|
||||
- 预置反射类型常量类,如 `StringClass`、`IntType` 等
|
||||
- 你可以直接使用 `String::class`、`Int::class` 等 Kotlin 的类引用进行替代,对于原始类型与包装类,`IntType` 等价于 `Int::class`,`IntClass` 等价于 `JInteger::class`
|
||||
|
||||
- `DexClassFinder` 功能
|
||||
- 由于其设计缺陷,且在 Android 平台上使用时可能存在性能问题,目前不再提供
|
||||
|
||||
- `RemedyPlan` 和 `method { ... } .remedys { ... }` 功能
|
||||
- 由于此功能存在可能的黑盒问题,维护相对困难,如需使用类似功能,请手动实现,不再提供
|
||||
|
||||
- `ClassLoader.listOfClasses()` 功能
|
||||
- 由于各个平台实现方案复杂且不稳定,不再提供
|
||||
|
||||
- `ClassLoader.searchClass()` 功能
|
||||
- 由于性能问题,且设计时仅限于 Android 平台使用,过滤条件维护相对困难,不再提供
|
||||
|
||||
- `Class.hasExtends`、`Class.extends`、`Class.implements` 功能
|
||||
- 你可以使用 `A::class isSubclassOf B::class` 来取代它们
|
||||
|
||||
- `Class.toJavaPrimitiveType()` 功能
|
||||
- 功能设计上存在概念混淆问题,不再提供
|
||||
|
||||
- `"com.some.clazz".hasClass(loader)` 功能
|
||||
- 你可以使用 `loader.hasClass("com.some.clazz")` 来取代它
|
||||
|
||||
- `Class.hasField`、`Class.hasMethod`、`Class.hasConstructor` 功能
|
||||
- 由于设计缺陷,不再提供
|
||||
|
||||
- `Class.hasModifiers(...)`、`Member.hasModifiers(...)` 功能
|
||||
- 你可以直接使用 `Class.isPublic`、`Member.isPublic` 等扩展方法来取代它们。
|
||||
|
||||
- `Class.generic()`、`GenericClass` 功能
|
||||
- 如果只是希望获取超类的泛型参数,你可以使用 `Class.genericSuperclassTypeArguments()`,由于设计缺陷,不再提供
|
||||
|
||||
- `Class.current()`、`CurrentClass` 功能
|
||||
- 已合并到 `KavaRef.resolve()` 的核心功能中,不再单独提供
|
||||
|
||||
- `Class.buildOf(...)` 功能
|
||||
- 你可以使用 `Class.createInstance(...)` 来取代它
|
||||
|
||||
- `Class.allMethods()`、`Class.allFields()`、`Class.allConstructors()` 功能
|
||||
- 由于其污染作用域,不再提供
|
||||
|
||||
- `YLog` 日志功能
|
||||
- `KavaRef` 不再接管日志,你可以使用对应平台的实现方式,不再提供
|
||||
|
||||
## 异常处理
|
||||
|
||||
`KavaRef` 在异常处理方面与 `YukiReflection` 完全不同,`KavaRef` 的异常逻辑将保持默认透明,<u>**它不再主动拦截异常并打印错误日志甚至是提供 `onNoSuchMethod` 监听**</u>,当没有过滤到任何有效的成员时,`KavaRef` 会直接抛出异常,除非你**明确声明条件为可选 (与 `YukiReflection` 逻辑保持一致)**。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// 假设这就是你的 MyClass 实例
|
||||
val myClass: MyClass
|
||||
// 使用 KavaRef 调用并执行
|
||||
MyClass::class
|
||||
.resolve()
|
||||
.optional() // 声明为可选,不要抛出异常
|
||||
// 使用 firstMethodOrNull 替代 firstMethod,因为找不到会抛出 Kotlin 自身的 NoSuchElementException
|
||||
.firstMethodOrNull {
|
||||
name = "doNonExistentMethod" // 假设这个方法不存在
|
||||
parameters(String::class)
|
||||
}?.of(myClass)?.invoke("Hello, KavaRef!")
|
||||
```
|
||||
|
||||
更多内容请参考 [kavaref-core](../library/kavaref-core.md) 中的 [异常处理](../library/kavaref-core.md#异常处理) 部分。
|
||||
|
||||
## 初次使用 KavaRef
|
||||
|
||||
如果你没用过 `YukiReflection` 或者 `YukiHookAPI`,没关系,你可以参考以下内容来快速上手。
|
||||
|
||||
::: tip 接下来做什么
|
||||
|
||||
更多内容,请继续阅读 [kavaref-core](../library/kavaref-core.md) 和 [kavaref-extension](../library/kavaref-extension.md)。
|
||||
|
||||
立即开始使用 `KavaRef` 吧!
|
||||
|
||||
:::
|
38
docs-source/src/zh-cn/config/processor-resolvers.md
Normal file
38
docs-source/src/zh-cn/config/processor-resolvers.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# 第三方 Member 解析器
|
||||
|
||||
> 这里收录了一些第三方的 Member 解析器,可供参考与使用。
|
||||
>
|
||||
> 使用方法请阅读 [自定义解析器](../library/kavaref-core.md#自定义解析器)。
|
||||
|
||||
## AndroidHiddenApiBypass
|
||||
|
||||
[项目地址](https://github.com/LSPosed/AndroidHiddenApiBypass)
|
||||
|
||||
> LSPass: Bypass restrictions on non-SDK interfaces
|
||||
|
||||
```kotlin
|
||||
class AndroidHiddenApiBypassResolver : MemberProcessor.Resolver() {
|
||||
|
||||
override fun <T : Any> getDeclaredConstructors(declaringClass: Class<T>): List<Constructor<T>> {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
||||
return super.getDeclaredConstructors(declaringClass)
|
||||
}
|
||||
|
||||
val constructors = HiddenApiBypass.getDeclaredMethods(declaringClass)
|
||||
.filterIsInstance<Constructor<T>>()
|
||||
.toList()
|
||||
return constructors
|
||||
}
|
||||
|
||||
override fun <T : Any> getDeclaredMethods(declaringClass: Class<T>): List<Method> {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
||||
return super.getDeclaredMethods(declaringClass)
|
||||
}
|
||||
|
||||
val methods = HiddenApiBypass.getDeclaredMethods(declaringClass)
|
||||
.filterIsInstance<Method>()
|
||||
.toList()
|
||||
return methods
|
||||
}
|
||||
}
|
||||
```
|
5
docs-source/src/zh-cn/config/r8-proguard.md
Normal file
5
docs-source/src/zh-cn/config/r8-proguard.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# R8 与 Proguard 混淆
|
||||
|
||||
> 大部分场景下 Android 应用程序安装包可通过混淆压缩体积,这里介绍了混淆规则的配置方法。
|
||||
|
||||
`KavaRef` 在 Android 项目中不需要额外配置任何混淆规则。
|
36
docs-source/src/zh-cn/guide/home.md
Normal file
36
docs-source/src/zh-cn/guide/home.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# 介绍
|
||||
|
||||
> `KavaRef` 是一个使用 Kotlin 实现的现代化 Java 反射 API。
|
||||
|
||||
## 背景
|
||||
|
||||
这是一个使用 Kotlin 实现的现代化 Java 反射 API,旨在提供更简洁、更易用的 API,同时保留 Java 反射的强大功能。
|
||||
|
||||
项目图标由 [MaiTungTM](https://github.com/Lagrio) 设计,名称取自 **K**otlinJ**avaRef**lection,意为使用 Kotlin 实现的 Java 反射。
|
||||
|
||||
它最早诞生于 [YukiHookAPI](https://github.com/HighCapable/YukiHookAPI),后期被解耦合为 [YukiReflection](https://github.com/HighCapable/YukiReflection) 项目。
|
||||
|
||||
如你所见,现在 `KavaRef` 是借助 `YukiReflection` 的设计思想完全重构的一套全新 API,它们没有从属关系,并将取代 `YukiReflection` 成为一个全新的反射解决方案。
|
||||
|
||||
如果你正在使用 `YukiReflection` 或与之相关的 `YukiHookAPI` 项目,你可以参考 [这里](../config/migration) 来迁移反射 API 的写法到 `KavaRef`。
|
||||
|
||||
## 用途
|
||||
|
||||
`KavaRef` 采用 Kotlin **lambda** 语法与 Java Builder 风格构建。
|
||||
|
||||
它能取代 [Java 原生的反射 API](https://pdai.tech/md/java/basic/java-basic-x-reflection.html),使用更加人性化的语言实现一套更加完善的反射方案。
|
||||
|
||||
## 技能要求
|
||||
|
||||
你必须已熟练掌握 Java 原生的反射 API,了解 Java 的类加载机制、字节码结构以及它们在 Kotlin 中的用法 (如果你正在使用 Kotlin)。
|
||||
|
||||
## 语言要求
|
||||
|
||||
推荐使用 Kotlin,API 代码构成同样支持 Java,但是在纯 Java 项目中 `KavaRef` 有可能无法发挥其全部功能和语法糖优势。
|
||||
|
||||
文档全部的 Demo 示例代码都将首先使用 Kotlin 进行描述,如果你完全不会使用 Kotlin 那你将有可能无法更全面地体验和使用 `KavaRef` 的功能。
|
||||
|
||||
## 功能贡献
|
||||
|
||||
本项目的维护离不开各位开发者的支持和贡献,目前这个项目处于初期阶段,可能依然存在一些问题或者缺少你需要的功能,
|
||||
如果可能,欢迎提交 PR 为此项目贡献你认为需要的功能或前往 [GitHub Issues](repo://issues) 向我们提出建议。
|
95
docs-source/src/zh-cn/guide/quick-start.md
Normal file
95
docs-source/src/zh-cn/guide/quick-start.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# 快速开始
|
||||
|
||||
> 集成 `KavaRef` 到你的项目中。
|
||||
|
||||
## 项目要求
|
||||
|
||||
项目需要使用 `IntelliJ IDEA` 或 `Android Studio` 创建且类型为 Java 或 Android 项目并已集成 Kotlin 环境依赖。
|
||||
|
||||
- IntelliJ IDEA (建议 [从这里](https://www.jetbrains.com/idea) 获取最新版本)
|
||||
|
||||
- Android Studio (建议 [从这里](https://developer.android.com/studio) 获取最新版本)
|
||||
|
||||
- Kotlin 1.9.0+、Gradle 8+、Java 17+
|
||||
|
||||
### 配置存储库
|
||||
|
||||
`KavaRef` 的依赖发布在 **Maven Central** 和我们的公共存储库中,你可以使用如下方式配置存储库。
|
||||
|
||||
我们推荐使用 Kotlin DSL 作为 Gradle 构建脚本语言并推荐使用 [SweetDependency](https://github.com/HighCapable/SweetDependency) 来管理依赖。
|
||||
|
||||
#### SweetDependency (推荐)
|
||||
|
||||
在你的项目 `SweetDependency` 配置文件中配置存储库。
|
||||
|
||||
```yaml
|
||||
repositories:
|
||||
google:
|
||||
maven-central:
|
||||
# (可选) 你可以添加此 URL 以使用我们的公共存储库
|
||||
# 当 Sonatype-OSS 发生故障无法发布依赖时,此存储库作为备选进行添加
|
||||
# 详情请前往:https://github.com/HighCapable/maven-repository
|
||||
highcapable-maven-releases:
|
||||
# 中国大陆用户请将下方的 "raw.githubusercontent.com" 修改为 "raw.gitmirror.com"
|
||||
url: https://raw.githubusercontent.com/HighCapable/maven-repository/main/repository/releases
|
||||
```
|
||||
|
||||
#### 传统方式
|
||||
|
||||
在你的项目 `build.gradle.kts` 中配置存储库。
|
||||
|
||||
```kotlin
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
// (可选) 你可以添加此 URL 以使用我们的公共存储库
|
||||
// 当 Sonatype-OSS 发生故障无法发布依赖时,此存储库作为备选进行添加
|
||||
// 详情请前往:https://github.com/HighCapable/maven-repository
|
||||
// 中国大陆用户请将下方的 "raw.githubusercontent.com" 修改为 "raw.gitmirror.com"
|
||||
maven("https://raw.githubusercontent.com/HighCapable/maven-repository/main/repository/releases")
|
||||
}
|
||||
```
|
||||
|
||||
### 配置 Java 版本
|
||||
|
||||
在你的项目 `build.gradle.kts` 中修改 Kotlin 的 Java 版本为 17 及以上。
|
||||
|
||||
> Java 项目
|
||||
|
||||
```kt
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(17)
|
||||
}
|
||||
```
|
||||
|
||||
> Android 项目
|
||||
|
||||
```kt
|
||||
android {
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 功能一览
|
||||
|
||||
整个项目分为多个模块,你可以选择你希望引入的模块作为依赖应用到你的项目中,但一定要包含 **kavaref-core** 模块。
|
||||
|
||||
你可以点击下方对应的模块前往查看详细的功能介绍。
|
||||
|
||||
- [kavaref-core](../library/kavaref-core.md)
|
||||
- [kavaref-extension](../library/kavaref-extension.md)
|
||||
|
||||
## Demo
|
||||
|
||||
你可以在 [这里](repo://tree/main/samples) 找到一些示例,查看对应的演示项目来更好地了解这些功能的运作方式,快速地挑选出你需要的功能。
|
38
docs-source/src/zh-cn/index.md
Normal file
38
docs-source/src/zh-cn/index.md
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
home: true
|
||||
title: 首页
|
||||
heroImage: /images/logo.svg
|
||||
actions:
|
||||
- text: 快速上手
|
||||
link: /zh-cn/guide/home
|
||||
type: primary
|
||||
- text: 更新日志
|
||||
link: /zh-cn/about/changelog
|
||||
type: secondary
|
||||
features:
|
||||
- title: 轻量优雅
|
||||
details: 拥有一套强大、优雅、人性化、完全使用 Kotlin lambda 打造的 API,可以帮你快速实现字节码的过滤以及反射功能。
|
||||
- title: 全面兼容
|
||||
details: 使用原生 Java API 实现反射功能,可在任何 Kotlin on JVM 的项目上使用,在 Android 上使用也丝毫不成问题。
|
||||
- title: 快速上手
|
||||
details: 简单易用,不需要繁琐的配置,不需要十足的开发经验,搭建环境集成依赖即可立即开始使用。
|
||||
footer: Apache-2.0 License | Copyright (C) 2019 HighCapable
|
||||
---
|
||||
|
||||
### 随时随地,开始反射。
|
||||
|
||||
```java
|
||||
public class World {
|
||||
|
||||
private void sayHello(String content) {
|
||||
System.out.println("Hello " + content + "!");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```kotlin
|
||||
World().resolve().firstMethod {
|
||||
name = "sayHello"
|
||||
parameters(String::class)
|
||||
}.invoke("KavaRef")
|
||||
```
|
768
docs-source/src/zh-cn/library/kavaref-core.md
Normal file
768
docs-source/src/zh-cn/library/kavaref-core.md
Normal file
@@ -0,0 +1,768 @@
|
||||
# kavaref-core
|
||||
|
||||

|
||||
<span style="margin-left: 5px"/>
|
||||

|
||||
|
||||
这是 KavaRef 的核心依赖,你需要引入此模块才能使用 KavaRef 的基本功能。
|
||||
|
||||
## 配置依赖
|
||||
|
||||
你可以使用以下方式将此模块添加到你的项目中。
|
||||
|
||||
### SweetDependency (推荐)
|
||||
|
||||
在你的项目 `SweetDependency` 配置文件中添加依赖。
|
||||
|
||||
```yaml
|
||||
libraries:
|
||||
com.highcapable.kavaref:
|
||||
kavaref-core:
|
||||
version: +
|
||||
```
|
||||
|
||||
在你的项目 `build.gradle.kts` 中配置依赖。
|
||||
|
||||
```kotlin
|
||||
implementation(com.highcapable.kavaref.kavaref.core)
|
||||
```
|
||||
|
||||
### Version Catalog
|
||||
|
||||
在你的项目 `gradle/libs.versions.toml` 中添加依赖。
|
||||
|
||||
```toml
|
||||
[versions]
|
||||
kavaref-core = "<version>"
|
||||
|
||||
[libraries]
|
||||
kavaref-core = { module = "com.highcapable.kavaref:kavaref-core", version.ref = "kavaref-core" }
|
||||
```
|
||||
|
||||
在你的项目 `build.gradle.kts` 中配置依赖。
|
||||
|
||||
```kotlin
|
||||
implementation(libs.kavaref.core)
|
||||
```
|
||||
|
||||
请将 `<version>` 修改为此文档顶部显示的版本。
|
||||
|
||||
### 传统方式
|
||||
|
||||
在你的项目 `build.gradle.kts` 中配置依赖。
|
||||
|
||||
```kotlin
|
||||
implementation("com.highcapable.kavaref:kavaref-core:<version>")
|
||||
```
|
||||
|
||||
请将 `<version>` 修改为此文档顶部显示的版本。
|
||||
|
||||
## 功能介绍
|
||||
|
||||
你可以 [点击这里](kdoc://kavaref-core) 查看 KDoc。
|
||||
|
||||
### 基本用法
|
||||
|
||||
KavaRef 采用链式调用的设计方案,它对可用的 Java 反射 API (例如 `Class`) 创建了扩展方法,你只需要对这些内容调用 `resolve()`,即可进入 KavaRef 的世界。
|
||||
|
||||
关系图如下。
|
||||
|
||||
``` :no-line-numbers
|
||||
KavaRef
|
||||
└── KClass/Class/Any.resolve()
|
||||
├── method()
|
||||
├── constructor()
|
||||
└── field()
|
||||
```
|
||||
|
||||
接下来,我们将给出多个示例的 Java `Class`,后续都将基于它们进行基本的反射方案讲解。
|
||||
|
||||
```java :no-line-numbers
|
||||
package com.demo;
|
||||
|
||||
public class BaseTest {
|
||||
|
||||
public BaseTest() {
|
||||
// ...
|
||||
}
|
||||
|
||||
private void doBaseTask(String taskName) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```java :no-line-numbers
|
||||
package com.demo;
|
||||
|
||||
public class Test extends BaseTest {
|
||||
|
||||
private Test() {
|
||||
// ...
|
||||
}
|
||||
|
||||
private static TAG = "Test";
|
||||
|
||||
private boolean isTaskRunning = false;
|
||||
|
||||
private void doTask(String taskName) {
|
||||
// ...
|
||||
}
|
||||
|
||||
private void release(String taskName, Function<boolean, String> task, boolean isFinish) {
|
||||
// ...
|
||||
}
|
||||
|
||||
private void stop() {
|
||||
// ...
|
||||
}
|
||||
|
||||
private String getName() {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
public class Box<T> {
|
||||
|
||||
public void print(T item, String str) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
假设,我们想要得到 `Test` 的 `doTask` 方法并执行,在 KavaRef 中,你可以通过以下方式来实现。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// 假设这就是这个 Class 的实例
|
||||
val test: Test
|
||||
// 通过实例化 Class 的方式对其进行反射
|
||||
// 在 KavaRef 中,你无需将其转换为 `java.lang.Class`,
|
||||
// 它会自动调用 KClass.java
|
||||
Test::class
|
||||
// 创建 KavaRef 反射
|
||||
.resolve()
|
||||
// 创建 Method (方法) 条件
|
||||
.method {
|
||||
// 设置方法名
|
||||
name = "doTask"
|
||||
// 设置方法参数类型
|
||||
parameters(String::class)
|
||||
}
|
||||
// 条件执行后会返回匹配到的 List<MethodResolver> 实例
|
||||
// 这里我们获取到过滤结果的第一个
|
||||
.first()
|
||||
// 在 MethodResolver 上设置 Test 的实例
|
||||
.of(test)
|
||||
// 调用方法并传入参数
|
||||
.invoke("task_name")
|
||||
```
|
||||
|
||||
在以上写法中,我们通过 `Test::class.resolve()` 来获取当前 `Class` 的 KavaRef 反射实例,
|
||||
然后通过 `method { ... }` 来创建一个方法过滤条件 `MethodCondition`,在其中设置方法名和参数类型,执行后返回 `List<MethodResolver>` 实例,
|
||||
接着我们通过 `first()` 来获取第一个匹配到的 `MethodResolver` 实例,
|
||||
然后通过 `of(test)` 来设置当前 `Class` 的实例,最后通过 `invoke("task_name")` 来执行方法并传入参数。
|
||||
|
||||
在这其中,`MethodCondition` 继承自 `MemberCondition`,它允许你对 `Method` 进行条件筛选,其中包含了 Java 核心的反射 API 的条件镜像,你可以查看对应的注释来了解每个 API 的原生用法。
|
||||
|
||||
同样地,`MethodResolver` 继承自 `MemberResolver`,它允许你对过滤结果中的 `Method` 进行反射调用。
|
||||
|
||||
由于这里的反射需求是得到一个可用的方法结果,所以 `method { ... }.first()` 的调用链可能来起来会比较繁琐,这个时候就有以下简化方案。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
Test::class
|
||||
.resolve()
|
||||
// 直接使用 firstMethod 来获取第一个匹配到的 MethodResolver 实例
|
||||
.firstMethod {
|
||||
name = "doTask"
|
||||
parameters(String::class)
|
||||
}
|
||||
.of(test)
|
||||
.invoke("task_name")
|
||||
```
|
||||
|
||||
由于我们现在可以拿到 `Test` 的实例,那么还有一种简化写法,你可以直接使用这个实例创建 KavaRef 反射。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// 在这里,Test 的实例 test 会被传给 KavaRef 并获取 test::class.java
|
||||
test.resolve()
|
||||
.firstMethod {
|
||||
name = "doTask"
|
||||
parameters(String::class)
|
||||
} // 由于你设置了实例,所以这里不再需要 of(test)
|
||||
.invoke("task_name")
|
||||
```
|
||||
|
||||
接下来,我们需要得到 `isTaskRunning` 变量,可以写作以下形式。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// 假设这就是这个 Class 的实例
|
||||
val test: Test
|
||||
// 使用 KavaRef 调用并执行
|
||||
val isTaskRunning = test.resolve()
|
||||
.firstField {
|
||||
name = "isTaskRunning"
|
||||
type = Boolean::class
|
||||
}.get<Boolean>()
|
||||
```
|
||||
|
||||
`Test` 中的构造方法是私有化的,现在,我们可以使用以下方式来创建它的实例。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
val test = Test::class.resolve()
|
||||
.firstConstructor {
|
||||
// 对于零参构造方法,可以使用以下条件过滤
|
||||
// 它等价于 parameterCount = 0
|
||||
emptyParameters()
|
||||
}.create() // 创建一个新的 Test 实例
|
||||
```
|
||||
|
||||
你也可以使用 `createAsType<T>()` 为实际对象 `Test` 指定其超类类型 `BaseTest`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
val test = Test::class.resolve()
|
||||
.firstConstructor {
|
||||
emptyParameters()
|
||||
}.createAsType<BaseTest>() // 创建一个新的 BaseTest 实例
|
||||
```
|
||||
|
||||
::: tip
|
||||
|
||||
除了 `firstMethod` 等方法外,你也可以使用 `lastMethod` 等方法来获取最后一个匹配到的 `MethodResolver` 实例,它等价于 `method { ... }.last()`。
|
||||
|
||||
在得到 `MemberResolver` 实例后,你可以使用 `self` 来获取当前 `MemberResolver` 的 `Member` 原始实例来对其进行一些你自己的操作。
|
||||
|
||||
在继承于 `InstanceAwareResolver` 的 `MemberResolver` 中 (例如 `MethodResolver` 和 `FieldResolver`),你都可以使用 `of(instance)`
|
||||
来设置当前实例,如果反射得到的是静态 (static) 成员,你无需设置实例。
|
||||
|
||||
:::
|
||||
|
||||
::: danger
|
||||
|
||||
在继承于 `InstanceAwareResolver` 的 `MemberResolver` 中,`of(instance)` 的类型要求与当前反射的 `Class` 实例泛型类型相同,
|
||||
除非不指定 `Class` 泛型类型,或将 `Class` 泛型类型设置为 `Any`。
|
||||
|
||||
如果 `of(instance)` 出现 `Required: Nothing?` 错误 (这通常由于 `Class` 通过 `Class.forName(...)` 或 `ClassLoader.loadClass(...)` 创建),
|
||||
则是你的 `Class` 为 `Class<*>` (Java 中是 `Class<?>`),此时如果你不想指定类型,请设置或转换为 `Class<Any>`,就像下面这样。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
val myClass = Class.forName("com.xxx.MyClass") as Class<Any>
|
||||
// 假设这就是这个 Class 的实例
|
||||
val myClassInstance: Any
|
||||
myClass.resolve()
|
||||
.firstMethod {
|
||||
// ...
|
||||
}.of(myClassInstance).invoke(...)
|
||||
```
|
||||
|
||||
你也可以使用 [kavaref-extension](kavaref-extension.md) 中提供的 [创建 Class 对象](kavaref-extension.md#创建-class-对象) 来解决这个问题。
|
||||
|
||||
:::
|
||||
|
||||
### 模糊条件
|
||||
|
||||
你会注意到 `Test` 中有一个 `release` 方法,但是它的方法参数很长,而且部分类型可能无法直接得到。
|
||||
|
||||
此时,你可以借助 `parameters(...)` 条件使用 `VagueType` 来填充你不想填写的方法参数类型。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// 假设这就是这个 Class 的实例
|
||||
val test: Test
|
||||
// 使用 KavaRef 调用并执行
|
||||
test.resolve()
|
||||
.firstMethod {
|
||||
name = "release"
|
||||
// 使用 VagueType 来填充不想填写的类型,同时保证其它类型能够匹配
|
||||
parameters(String::class, VagueType, Boolean::class)
|
||||
} // 得到这个方法
|
||||
```
|
||||
|
||||
::: warning
|
||||
|
||||
`VagueType` 只能在有多个参数的过滤条件时使用,它不可以在只能设置单个参数的过滤条件中使用,例如 `type`。
|
||||
|
||||
你可以使用 `VagueType`、`VagueType::class` 或 `VagueType::class.java` 来创建,它们都能被正确识别为模糊过滤条件。
|
||||
|
||||
:::
|
||||
|
||||
### 自由条件
|
||||
|
||||
在 `MemberCondition` 中,`name`、`type`、`parameterCount` 等条件都可以使用 Kotlin lambda 特性创建自由过滤条件。
|
||||
|
||||
假设我们要得到 `Test` 中的 `doTask` 方法,可以使用以下实现。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// 假设这就是这个 Class 的实例
|
||||
val test: Test
|
||||
// 使用 KavaRef 调用并执行
|
||||
test.resolve()
|
||||
.firstMethod {
|
||||
// 使用 lambda 来设置方法名
|
||||
name {
|
||||
// 设置名称不区分大小写
|
||||
it.equals("dotask", ignoreCase = true)
|
||||
}
|
||||
// 设置参数类型
|
||||
parameters(String::class)
|
||||
}.invoke("task_name")
|
||||
```
|
||||
|
||||
### 泛型条件
|
||||
|
||||
KavaRef 支持添加泛型过滤条件,你可以使用 `TypeMatcher` 提供的相关功能来实现。
|
||||
|
||||
假设我们需要过滤 `Box<String>` 中的 `print` 方法。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// 假设这就是这个 Class 的实例
|
||||
val box: Box<String>
|
||||
// 使用 KavaRef 调用并执行
|
||||
box.resolve()
|
||||
.firstMethod {
|
||||
name = "print"
|
||||
// 设置泛型参数条件
|
||||
genericParametes(
|
||||
// 过滤泛型名称 "T"
|
||||
typeVar("T"),
|
||||
// 通过 Class 创建 TypeMatcher
|
||||
String::class.toTypeMatcher()
|
||||
)
|
||||
}.invoke("item", "str")
|
||||
```
|
||||
|
||||
### 在超类过滤
|
||||
|
||||
你会注意到 `Test` 继承于 `BaseTest`,现在我们想得到 `BaseTest` 的 `doBaseTask` 方法。
|
||||
|
||||
在不知道超类名称的情况下,我们只需要在过滤条件中加入 `superclass()` 即可实现这个功能。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// 假设这就是这个 Class 的实例
|
||||
val test: Test
|
||||
// 使用 KavaRef 调用并执行
|
||||
test.resolve()
|
||||
.firstMethod {
|
||||
name = "doBaseTask"
|
||||
parameters(String::class)
|
||||
// 只需要添加这个条件
|
||||
superclass()
|
||||
}.invoke("task_name")
|
||||
```
|
||||
|
||||
这个时候我们就可以在超类中获取到这个方法了。
|
||||
|
||||
::: tip
|
||||
|
||||
`superclass()` 一旦设置就会自动循环向后过滤全部继承的超类中是否有这个方法,直到过滤到目标没有超类 (继承关系为 `java.lang.Object`) 为止。
|
||||
|
||||
:::
|
||||
|
||||
::: danger
|
||||
|
||||
当前过滤的方法除非指定 `superclass()` 条件,否则只能过滤到当前 `Class` 的方法,这是 Java 反射 API 的默认行为,
|
||||
KavaRef 会调用 `Class.getDeclaredMethods()` 来获取当前 `Class` 的方法而不是 `Class.getMethods()`。
|
||||
|
||||
:::
|
||||
|
||||
### 更多条件
|
||||
|
||||
KavaRef 提供了一些过滤条件来辅助 Java 反射 API 的使用。
|
||||
|
||||
假设我们要得到 `Test` 中的静态变量 `TAG` 的内容。
|
||||
|
||||
为了体现过滤的条件包含静态描述符 (static),我们可以使用以下方式来实现。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
val tag = Test::class.resolve()
|
||||
.firstField {
|
||||
name = "TAG"
|
||||
type = String::class
|
||||
// 创建描述符过滤
|
||||
modifiers(Modifiers.STATIC)
|
||||
// 或者
|
||||
modifiers {
|
||||
it.contains(Modifiers.STATIC)
|
||||
}
|
||||
}.get<String>() // 获取字段内容
|
||||
```
|
||||
|
||||
你还可以在 `type`、`parameters` 等条件中使用字符串类型传入完整类名。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// 假设这就是这个 Class 的实例
|
||||
val test: Test
|
||||
// 使用 KavaRef 调用并执行
|
||||
test.resolve()
|
||||
.firstMethod {
|
||||
name = "doTask"
|
||||
// 使用字符串类型传入完整类名
|
||||
parameters("java.lang.String")
|
||||
}.invoke("task_name")
|
||||
```
|
||||
|
||||
### 异常处理
|
||||
|
||||
在默认情况下,KavaRef 会在反射调用过程中找不到成员时抛出异常。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
Test::class.resolve()
|
||||
.method {
|
||||
name = "doNonExistentMethod"
|
||||
} // 这里会抛出 NoSuchMethodException
|
||||
```
|
||||
|
||||
如果你不希望抛出异常,可以设置可选条件 `optional()`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
Test::class.resolve()
|
||||
// 设置可选条件
|
||||
.optional()
|
||||
.method {
|
||||
name = "doNonExistentMethod"
|
||||
} // 返回空的 List<MethodResolver>
|
||||
```
|
||||
|
||||
KavaRef 会打印完整的异常内容以供调试,在使用 `optional()` 时,异常会以 WARN 级别的日志打印。
|
||||
|
||||
> 示例如下
|
||||
|
||||
``` :no-line-numbers
|
||||
No method found matching the condition for current class.
|
||||
+------------------------------------------------+
|
||||
| class com.demo |
|
||||
+------------+-----------------------------------+
|
||||
| name | doNonExistentMethod |
|
||||
| parameters | [class java.lang.String, boolean] |
|
||||
+------------+-----------------------------------+
|
||||
```
|
||||
|
||||
如果你不希望 KavaRef 抛出或打印任何内容,你可以使用 `optional(silent = true)` 静默化处理,但是我们**不建议这样做**,这会掩盖问题,除非有必要这么做。
|
||||
|
||||
::: danger
|
||||
|
||||
如果你设置了 `optional()`,那么请不要使用 `firstMethod`、`firstConstructor` 等方法来获取单个结果,
|
||||
因为它们会在没有结果时抛出列表为空的异常,你可以使用后缀为 `OrNull` 的方法来获取单个结果。
|
||||
|
||||
:::
|
||||
|
||||
### 日志管理
|
||||
|
||||
KavaRef 提供了其自身的日志管理功能,你可以通过 `KavaRef.logLevel` 来设置日志级别。
|
||||
|
||||
你可以设置 `KavaRef.logLevel = KavaRefRuntime.LogLevel.DEBUG` 来启用 DEBUG 级别的日志使得 KavaRef 在过滤过程向控制台打印更为详细的分步过滤条件日志。
|
||||
|
||||
如果你想关闭 KavaRef 的全部日志打印,你可以设置 `KavaRef.logLevel = KavaRefRuntime.LogLevel.OFF`。
|
||||
|
||||
如果你有更高级的需求,你可以实现 `KavaRefRuntime.Logger` 来自定义自己的日志打印方式。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
class MyLogger : KavaRefRuntime.Logger {
|
||||
|
||||
// 在这里可以指定日志打印的标签
|
||||
override val tag = "MyLogger"
|
||||
|
||||
override fun debug(msg: Any?, throwable: Throwable?) {
|
||||
// 在这里实现你的日志打印逻辑
|
||||
}
|
||||
|
||||
override fun info(msg: Any?, throwable: Throwable?) {
|
||||
// 在这里实现你的日志打印逻辑
|
||||
}
|
||||
|
||||
override fun warn(msg: Any?, throwable: Throwable?) {
|
||||
// 在这里实现你的日志打印逻辑
|
||||
}
|
||||
|
||||
override fun error(msg: Any?, throwable: Throwable?) {
|
||||
// 在这里实现你的日志打印逻辑
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
然后,将其设置到 KavaRef 上即可。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
KavaRef.setLogger(MyLogger())
|
||||
```
|
||||
|
||||
### 进阶用法
|
||||
|
||||
上述内容讲解的均为标准场景下的使用方法,如果你有更加细粒度的使用场景,你可以手动创建 KavaRef 的相关组件。
|
||||
|
||||
如果你不喜欢 Kotlin lambda 的写法,你可以手动创建链式调用。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// 假设这就是这个 Class 的实例
|
||||
val test: Test
|
||||
// 使用 KavaRef 调用并执行
|
||||
test.resolve()
|
||||
.method() // 条件开始
|
||||
.name("doTask")
|
||||
.parameters(String::class)
|
||||
.build() // 条件结束 (执行)
|
||||
.first()
|
||||
.invoke("task_name")
|
||||
```
|
||||
|
||||
你还可以手动创建任何过滤条件以实现在任何反射中复用它。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// 假设这就是这个 Class 的实例
|
||||
val test: Test
|
||||
// 手动创建 MethodCondition
|
||||
val condition = MethodCondition<Test>()
|
||||
condition.name = "doTask"
|
||||
condition.parameters(String::class)
|
||||
// 应用条件到反射对象
|
||||
Test::class.resolve()
|
||||
.firstMethod(condition)
|
||||
.of(test) // 设置实例
|
||||
.invoke("task_name")
|
||||
```
|
||||
|
||||
或者,你还可以手动完整地实现整个反射过程。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// 假设这就是这个 Class 的实例
|
||||
val test: Test
|
||||
// 手动创建 MethodCondition
|
||||
val condition = MethodCondition<Test>()
|
||||
condition.name = "doTask"
|
||||
condition.parameters(String::class)
|
||||
// 手动创建 MemberCondition.Configuration
|
||||
val configuration = Test::class.java.createConfiguration(
|
||||
memberInstance = test, // 设置实例
|
||||
processorResolver = null, // 使用默认的解析器,可参考下方的 "自定义解析器"
|
||||
superclass = false, // 是否在超类中过滤
|
||||
optional = MemberCondition.Configuration.Optional.NO // 配置可选条件
|
||||
)
|
||||
// 创建并开始过滤
|
||||
val resolvers = condition.build(configuration)
|
||||
// 获取第一个结果
|
||||
val resolver = resolvers.first()
|
||||
// 执行方法
|
||||
resolver.invoke("task_name")
|
||||
```
|
||||
|
||||
如果你对业务层逻辑有更高级的需求,你还可以使用 `mergeWith` 来合并多个过滤条件。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// 假设这就是这个 Class 的实例
|
||||
val test: Test
|
||||
// 手动创建 MethodCondition
|
||||
// 创建第一个条件
|
||||
val condition1 = MethodCondition<Test>()
|
||||
condition1.name = "doTask"
|
||||
// 创建第二个条件
|
||||
val condition2 = MethodCondition<Test>()
|
||||
condition2.parameters(String::class)
|
||||
// 将 condition2 合并到 condition1 中
|
||||
// 此时 condition1 的条件将包含 condition2 不为 null 的条件,
|
||||
// condition1 中重复的条件将被 condition2 的条件覆盖
|
||||
condition1.mergeWith(condition2)
|
||||
// 你还可以使用 infix 语法
|
||||
condition1 mergeWith condition2
|
||||
// 使用 KavaRef 调用并执行
|
||||
Test::class.resolve()
|
||||
.firstMethod(condition1)
|
||||
.of(test)
|
||||
.invoke("task_name")
|
||||
```
|
||||
|
||||
::: danger
|
||||
|
||||
当 `MemberCondition` 已经设置 `MemberCondition.Configuration` 时将不再允许重复使用 `build(...)` 进行创建,
|
||||
此时你需要使用 `copy()` 来复制并创建一份新的 `MemberCondition`。
|
||||
|
||||
同样地,`InstanceAwareResolver` 在通过 `MemberCondition.Configuration.memberInstance` 或 `of(instance)` 设置实例后也不允许重复设置新的实例,
|
||||
此时你也需要使用 `copy()` 来复制并创建一份新的 `InstanceAwareResolver`。
|
||||
|
||||
:::
|
||||
|
||||
### 自定义解析器
|
||||
|
||||
KavaRef 使用默认的 `Member` 解析器进行过滤操作,如果你想实现自己的解析器,你可以自定义全局和每一个反射过程使用的解析器。
|
||||
|
||||
你可以继承于 `MemberProccessor.Resolver` 来实现自己的解析器。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
class MyMemberProcessorResolver : MemberProcessor.Resolver() {
|
||||
|
||||
override fun <T : Any> getDeclaredConstructors(declaringClass: Class<T>): List<Constructor<T>> {
|
||||
// 在这里拦截并实现你的构造方法过滤逻辑
|
||||
return super.getDeclaredConstructors(declaringClass)
|
||||
}
|
||||
|
||||
override fun <T : Any> getDeclaredMethods(declaringClass: Class<T>): List<Method> {
|
||||
// 在这里拦截并实现你的方法过滤逻辑
|
||||
return super.getDeclaredMethods(declaringClass)
|
||||
}
|
||||
|
||||
override fun <T : Any> getDeclaredFields(declaringClass: Class<T>): List<Field> {
|
||||
// 在这里拦截并实现你的字段过滤逻辑
|
||||
return super.getDeclaredFields(declaringClass)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
然后你可以将其设置到全局配置中。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
MemberProcessor.globalResolver = MyMemberProcessorResolver()
|
||||
```
|
||||
|
||||
或者,在每次反射过程中,你可以使用 `MemberCondition.Configuration` 来设置自定义解析器,或者使用链式调用设置解析器。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// 创建解析器
|
||||
val myResolver = MyMemberProcessorResolver()
|
||||
// 假设这就是这个 Class 的实例
|
||||
val test: Test
|
||||
// 使用 KavaRef 调用并执行
|
||||
test.resolve()
|
||||
// 设置自定义解析器
|
||||
.processor(myResolver)
|
||||
.firstMethod {
|
||||
name = "doTask"
|
||||
parameters(String::class)
|
||||
}.invoke("task_name")
|
||||
```
|
||||
|
||||
::: tip
|
||||
|
||||
你可以在 [这里](../config/processor-resolvers.md) 找到一些公开维护的自定义解析器,定义在你的项目中即可使用。
|
||||
|
||||
:::
|
||||
|
||||
### 关于缓存
|
||||
|
||||
由于过滤条件的多样性,KavaRef 不直接提供缓存功能,根据每个开发者的实现方式不同,缓存的实现方式也会有所不同。
|
||||
|
||||
我们建议手动对过滤结果创建的 `MemberResolver` 实现缓存以提高性能并参考 [手动创建](#手动创建) 拆分过滤条件以优化代码复用率。
|
||||
|
||||
::: danger
|
||||
|
||||
如果你使用了 `val myResolver by lazy { ... }` 来实现缓存,例如下方这样做。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
val myResolver by lazy {
|
||||
Test::class.resolve()
|
||||
.firstMethod {
|
||||
name = "doTask"
|
||||
parameters(String::class)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
你在调用时可能会这样做。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// 假设这就是这个 Class 的实例
|
||||
val test: Test
|
||||
// 使用 KavaRef 调用并执行
|
||||
myResolver.of(test).invoke("task_name")
|
||||
```
|
||||
|
||||
请注意,由于 `MemberResolver` 已被缓存,在你每次引用它时调用的是同一个实例,而 `MemberResolver` 的实例对象不允许重复设置 (参考 [手动创建](#手动创建) 下方的 “特别注意”),
|
||||
所以直接这样调用会抛出异常,你需要改为以下形式。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// 假设这就是这个 Class 的实例
|
||||
val test: Test
|
||||
// 使用 KavaRef 调用并执行
|
||||
myResolver.copy().of(test).invoke("task_name")
|
||||
```
|
||||
|
||||
这样一来,你就可以在每次调用时复制一个新的 `MemberResolver` 实例而不需要重复反射过程,也不会抛出异常。
|
||||
|
||||
:::
|
||||
|
||||
### Java 用法
|
||||
|
||||
KavaRef 不推荐直接在 Java 中使用,因为它的 API 设计是基于 Kotlin 的特性和语法糖。
|
||||
|
||||
如果你需要在 Java 中使用 KavaRef,你可以使用以下方式来实现。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```java
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 假设这就是这个 Class 的实例
|
||||
Test test;
|
||||
// 使用 KavaRef 调用并执行
|
||||
KavaRef.resolveClass(Test.class)
|
||||
.method()
|
||||
.name("doTask")
|
||||
.parameters(String.class)
|
||||
.build()
|
||||
.get(0)
|
||||
.of(test)
|
||||
.invoke("task_name");
|
||||
// 或者,使用实例创建 KavaRef 反射
|
||||
KavaRef.resolveObject(test)
|
||||
.method()
|
||||
.name("doTask")
|
||||
.parameters(String.class)
|
||||
.build()
|
||||
.get(0)
|
||||
.invoke("task_name");
|
||||
}
|
||||
}
|
||||
```
|
295
docs-source/src/zh-cn/library/kavaref-extension.md
Normal file
295
docs-source/src/zh-cn/library/kavaref-extension.md
Normal file
@@ -0,0 +1,295 @@
|
||||
# kavaref-extension
|
||||
|
||||

|
||||
<span style="margin-left: 5px"/>
|
||||

|
||||
|
||||
这是 KavaRef 相关功能的扩展依赖。
|
||||
|
||||
## 配置依赖
|
||||
|
||||
你可以使用如下方式将此模块添加到你的项目中。
|
||||
|
||||
### SweetDependency (推荐)
|
||||
|
||||
在你的项目 `SweetDependency` 配置文件中添加依赖。
|
||||
|
||||
```yaml
|
||||
libraries:
|
||||
com.highcapable.kavaref:
|
||||
kavaref-extension:
|
||||
version: +
|
||||
```
|
||||
|
||||
在你的项目 `build.gradle.kts` 中配置依赖。
|
||||
|
||||
```kotlin
|
||||
implementation(com.highcapable.kavaref.kavaref.extension)
|
||||
```
|
||||
|
||||
### Version Catalog
|
||||
|
||||
在你的项目 `gradle/libs.versions.toml` 中添加依赖。
|
||||
|
||||
```toml
|
||||
[versions]
|
||||
kavaref-extension = "<version>"
|
||||
|
||||
[libraries]
|
||||
kavaref-extension = { module = "com.highcapable.kavaref:kavaref-extension", version.ref = "kavaref-extension" }
|
||||
```
|
||||
|
||||
在你的项目 `build.gradle.kts` 中配置依赖。
|
||||
|
||||
```kotlin
|
||||
implementation(libs.kavaref.extension)
|
||||
```
|
||||
|
||||
请将 `<version>` 修改为此文档顶部显示的版本。
|
||||
|
||||
### 传统方式
|
||||
|
||||
在你的项目 `build.gradle.kts` 中配置依赖。
|
||||
|
||||
```kotlin
|
||||
implementation("com.highcapable.kavaref:kavaref-extension:<version>")
|
||||
```
|
||||
|
||||
请将 `<version>` 修改为此文档顶部显示的版本。
|
||||
|
||||
## 功能介绍
|
||||
|
||||
你可以 [点击这里](kdoc://kavaref-extension) 查看 KDoc。
|
||||
|
||||
### Class 扩展
|
||||
|
||||
KavaRef 提供了一些扩展,在处理 `Class` 对象时会更加方便。
|
||||
|
||||
KavaRef 对 `Class` 的扩展同样添加了 `KClass` 扩展,作用是调用 `KClass.java`,写法上比直接使用 `Some::class.java` 更加简洁。
|
||||
|
||||
#### 创建 Class 对象
|
||||
|
||||
例如我们需要使用字符串类名创建一个 `Class` 对象。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
val myClass = "com.example.MyClass".toClass()
|
||||
// 你可以使用带有 OrNull 后缀的方法在找不到 Class 时返回 null 而不是抛出异常
|
||||
val myClassOrNull = "com.example.MyClass".toClassOrNull()
|
||||
```
|
||||
|
||||
这些方法统一使用 `ClassLoaderProvider` 来获取默认的 `ClassLoader`,你可以设置默认的 `ClassLoader` 以影响全局功能。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
ClassLoaderProvider.classLoader = MyCustomClassLoader()
|
||||
```
|
||||
|
||||
你也可以手动向 `toClass` 方法传入一个 `ClassLoader` 参数来指定使用哪个 `ClassLoader`。
|
||||
|
||||
#### Class 对象引用
|
||||
|
||||
在 Kotlin 中引用 Java Class 需要写很长的声明,例如 `MyClass::class.java`,此时你可以用以下方式来简化。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
val myClass = classOf<MyClass>()
|
||||
```
|
||||
|
||||
你可以使用 `isSubclassOf` 方法来判断一个 `Class` 是否是另一个 `Class` 的子类。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
val isSubclass = MyClass::class isSubclassOf MySuperClass::class
|
||||
// 当然,它也有一个对应的反义判断方法
|
||||
val isNotSubclass = MyClass::class isNotSubclassOf MySuperClass::class
|
||||
```
|
||||
|
||||
你还可以使用 `hasSuperclass` 和 `hasInterfaces` 方法来判断一个 `Class` 是否有超类或接口。
|
||||
|
||||
::: danger
|
||||
|
||||
`classOf` 方法传入的 `Class` 默认会进行 Java 包装类的拆箱操作,无论你传入的是类似 `kotlin.Boolean` 还是 `java.lang.Boolean` (参考下方的 [Java 包装类扩展](#java-包装类扩展)),
|
||||
如果你需要避免传入的 `Class` 被拆箱变为原始类型,你需要明确设置 `primitiveType = false` 参数。
|
||||
|
||||
:::
|
||||
|
||||
#### 创建新的实例
|
||||
|
||||
KavaRef 为 `Class` 提供了一个方法来方便地创建一个新的实例,你不需要考虑构造参数的类型,你只需要传入对应的参数即可立即创建一个新的实例。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
val myClass = MyClass::class.createInstance("Hello", 123)
|
||||
// 你也可以使用带有 OrNull 后缀的方法在创建失败时返回 null 而不是抛出异常
|
||||
val myClassOrNull = MyClass::class.createInstanceOrNull("Hello", 123)
|
||||
// createInstance 方法默认仅过滤公开的构造方法,如果你需要调用非公有构造方法,请设置 isPublic = false
|
||||
val myClassWithPrivateConstructor = MyClass::class.createInstance("Private!", isPublic = false)
|
||||
// 如果你想指定创建实例的类型使用另一个类型,可以使用以下方法
|
||||
val mySuperClass = MyClas::class.createInstanceAsType<MySuperClass>("Hello", 123)
|
||||
// 同样地,你也可以使用带有 OrNull 后缀的方法在创建失败时返回 null 而不是抛出异常
|
||||
val mySuperClassOrNull = MyClass::class.createInstanceAsTypeOrNull<MySuperClass>("Hello", 123)
|
||||
```
|
||||
|
||||
::: tip
|
||||
|
||||
`createInstance` 方法在成功匹配一次后,会将结果进行缓存防止重复反射造成的性能损耗,它是线程安全的,你可以放心在任何标准场景下使用。
|
||||
|
||||
:::
|
||||
|
||||
::: danger
|
||||
|
||||
当你传入带有 `null` 的参数时,KavaRef 会尝试将其作为可匹配到条件的一部分 (模糊条件),准确性可能会下降。
|
||||
|
||||
`createInstance` 方法不允许所有参数均为 `null` 的情况 (条件完全模糊),会直接抛出异常,因为这种情况无法确定要创建哪个实例。
|
||||
|
||||
:::
|
||||
|
||||
#### Class 修饰符
|
||||
|
||||
KavaRef 也对 `Modifier` 进行了扩展,你可以直接使用 `Class.isPublic` 等方法来判断一个 `Class` 的修饰符。
|
||||
|
||||
#### VariousClass
|
||||
|
||||
KavaRef 提供了 `VariousClass` 类来装载不确定完整类名的 `Class` 对象,并返回成功匹配到的第一个。
|
||||
|
||||
此功能通常可用于 Android 应用中那些被 R8 混淆后的类名。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// 假设在 A 版本中,这个类为 com.example.a,
|
||||
// 在 B 版本中,这个类为 com.example.b
|
||||
val myClass = VariousClass("com.example.a", "com.example.b").load()
|
||||
// 你也可以使用后缀名为 OrNull 的方法在找不到 Class 时返回 null 而不是抛出异常
|
||||
val myClassOrNull = VariousClass("com.example.a", "com.example.b").loadOrNull()
|
||||
```
|
||||
|
||||
#### 延迟装载 Class 对象
|
||||
|
||||
KavaRef 提供了 `LazyClass` 类来延迟装载 `Class` 对象。
|
||||
|
||||
你可以在需要时再装载 `Class`,而不是在创建时就立即装载,这可以解决一些需要运行时或运行到特定条件下才需要装载的 `Class`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// 定义一个不可为空延迟装载的 Class 并托管给 myClass
|
||||
val myClass by lazyClass("com.example.MyClass")
|
||||
// 定义一个可为空延迟装载的 Class 并托管给 myClassOrNull
|
||||
val myClassOrNull by lazyClassOrNull("com.example.MyClass")
|
||||
// 它亦可支持传入 VariousClass
|
||||
val otherClassOrNull by lazyClassOrNull(VariousClass("com.example.a", "com.example.b"))
|
||||
// 在需要时调用即装载
|
||||
myClass.resolve()
|
||||
myClassOrNull?.resolve()
|
||||
otherClassOrNull?.resolve()
|
||||
```
|
||||
|
||||
#### ClassLoader 扩展
|
||||
|
||||
KavaRef 还为 `ClassLoader` 提供了一些实用的扩展方法。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// 假设这就是你的 ClassLoader
|
||||
val classLoader: ClassLoader
|
||||
// 装载一个 Class,在装载失败时返回 null
|
||||
val myClassOrNull = classLoader.loadClassOrNull("com.example.MyClass")
|
||||
// 判断这个 Class 是否存在于当前 ClassLoader 中
|
||||
val isClassExists = classLoader.hasClass("com.example.MyClass")
|
||||
```
|
||||
|
||||
### 数组 Class 扩展
|
||||
|
||||
在 Java 中,数组的 `Class` 对象是一个特殊的 `Class` 对象,通常,我们创建它的方式如下。
|
||||
|
||||
例如创建一个 `java.lang.String[]` 的 `Class` 对象。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
val arrayClass = java.lang.reflect.Array.newInstance(String::class.java, 0).javaClass
|
||||
```
|
||||
|
||||
这样写起来非常长,而且不方便维护,所以 KavaRef 提供了一个方法来简化这个过程。
|
||||
|
||||
现在,创建 `java.lang.String[]` 的 `Class` 对象可以这样写。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
val arrayClass = ArrayClass(String::class)
|
||||
```
|
||||
|
||||
### Member 扩展
|
||||
|
||||
KavaRef 提供了一些扩展方法来简化对 `Member` 的操作。
|
||||
|
||||
你可以在任何 `Member` 对象上使用 `makeAccessible` 方法来设置其可访问性。
|
||||
|
||||
如果 `Member` 是 `AccessibleObject` 类型即可生效。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// 假设这个是你当前的 Member 对象
|
||||
val method: Method
|
||||
// 设置方法可访问
|
||||
method.makeAccessible()
|
||||
```
|
||||
|
||||
同样地,KavaRef 也对 `Modifier` 进行了扩展,你可以直接使用 `Member.isPublic` 等方法来判断一个 `Member` 的修饰符。
|
||||
|
||||
### Type 扩展
|
||||
|
||||
在 Java 中操作类型或泛型类型时,通常需要使用 `Type` 接口及其子接口来处理。
|
||||
|
||||
KavaRef 提供了一些扩展方法来简化对 `Type` 的操作。
|
||||
|
||||
例如,你可以将一个符合要求的 `Type` 转换为 `Class` 对象。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
val type: Type
|
||||
val clazz = type.toClass()
|
||||
// 你也可以使用后缀名为 OrNull 的方法在转换失败时返回 null 而不是抛出异常
|
||||
val clazzOrNull = type.toClassOrNull()
|
||||
```
|
||||
|
||||
你也可以将符合要求的 `Type` 转换为 `ParameterizedType` 对象。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
val type: Type
|
||||
val parameterizedType = type.asParameterizedType()
|
||||
// 你也可以使用后缀名为 OrNull 的方法在转换失败时返回 null 而不是抛出异常
|
||||
val parameterizedTypeOrNull = type.asParameterizedTypeOrNull()
|
||||
```
|
||||
|
||||
你还可以使用以下方式获取超类中的泛型参数数组,这在一些超类与子类的封装操作中会经常用到。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
val myClass: Class<*>
|
||||
// 获取 myClass 的超类的泛型参数数组,获取失败或无法获取时将返回空数组
|
||||
val arguments = myClass.genericSuperclassTypeArguments()
|
||||
```
|
||||
|
||||
### Java 包装类扩展
|
||||
|
||||
在 Kotlin 中直接使用 `Boolean::class`、`Byte::class` 等方式获取到的是 Java 的原始类型 `boolean`、`byte` 而不是它们的包装类。
|
||||
|
||||
如果你需要获取 Java 的包装类,你需要使用完整的 `java.lang.Boolean::class`、`java.lang.Byte::class` 等方式或使用 `Boolean::class.javaObjectType`、`Byte::class.javaObjectType`。
|
||||
|
||||
所以,KavaRef 提供了一些类型别名来处理 Java 的包装类,现在你只需要在这些类型加上 `J` 前缀即可,例如 `JBoolean::class`,
|
||||
它等价于 `java.lang.Boolean::class`,部分类型需要填写全称,例如 `JInteger::class`。
|
Reference in New Issue
Block a user