@@ -6,195 +6,244 @@ EasyReflect对常规的反射操作进行封装。让使用反射操作变得简
66
77## 用法
88
9- ### 使用EasyReflect
10-
11- EasyReflect操作类包装了两部分数据:
9+ ### 1. 初识EasyReflect
1210
1311```
14- // 1. clazz:类的Class数据。总是存在
15- // 2. instance:具体的对象实例。可能不存在。当进行的操作会需要使用此数据时。会自动匹配默认构造器进行创建
1612class EasyReflect private constructor(val clazz: Class<*>, var instance:Any?)
1713```
1814
19- #### 创建EasyReflect实例的几种方式:
15+ 可以看到:EasyReflect本身持有两部分数据: ` clazz ` 与 ` instance ` .
2016
21- 1 . 通过指定class创建实例。此种方式创建数据** 不存在** 具体的instance实例
17+ - clazz: 此实例所绑定操作的clazz实例。永不为null
18+ - instance: 此实例所绑定的instance实例,为clazz类型的具体实例。可能为null。
2219
23- ```
24- var reflect = EasyReflect.create(clazz:Class<*>)
25- ```
20+ ** 请注意:对于instance数据来说,当执行操作过程中需要使用此instance实例时(比如读取某个成员变量),若此时instance为null,则将触发使用默认的空构造器进行instance创建。**
2621
27- 2 . 通过具体数据对象创建实例:此种方式创建数据** 存在** 具体的instance实例
22+ ### 2. 创建EasyReflect的几种姿势
23+
24+ 1 . ** 只使用Class进行创建** :创建后只含有clazz数据
2825
2926```
30- var reflect = EasyReflect.create(any:Any )
27+ val reflect = EasyReflect.create(Test::class.java )
3128```
3229
33- 3 . 通过完整的对象名与ClassLoader创建实例:此种方式创建数据 ** 不存在 ** 具体的instance实例
30+ 2 . ** 使用类全名进行创建(可指定ClassLoader) ** :创建后只含有clazz数据
3431
3532```
36- var reflect = EasyReflect.create(name:String, loader:ClassLoader?)
33+ val reflect = EasyReflect.create("com.haoge.sample.Test")
34+ // 也可指定ClassLoader进行加载
35+ val reflect = EasyReflect.create(className, classLoader)
3736```
3837
39- 4 . 通过具体的构造器进行实例创建:此种方式创建数据 ** 存在 ** 具体的instance实例
38+ 3 . ** 直接通过指定实例进行创建 ** :创建后clazz与instance均存在, clazz为instance.javaClass数据
4039
4140```
42- // 可变参数args需要与具体的构造器匹配
43- var reflect = EasyReflect.create(clazz).instance(varage args:Any?)
41+ val reflect = EasyReflect.create(any)
4442```
4543
46- #### 对字段进行取值/赋值
44+ 了解了EasyReflect的几种创建方式与其区别后。我们就可以正式进行使用了
45+
46+ ### 3. 调用指定构造器进行实例创建
4747
4848```
49- var reflect = ...
50- // 取值
51- reflect.getFieldValue(name:String)
52- // 赋值
53- reflect.setField(name:String, value:Any?)
49+ val reflect:EasyReflect = createReflect()
50+ // 通过默认空构造器进行对象创建
51+ val instance1 = EasyReflect.instance().instance
52+ // 通过与可变参数类型匹配的构造器进行对象创建
53+ val instance2 = EasyReflect.instance(arg0, arg1...argN).instance
5454```
5555
56- #### 对方法进行调用
56+ ### 4. 字段的赋值与取值
5757
58- ```
59- var reflect = ...
60- // 仅调用:name为方法名。args为所需参数数据
61- reflect.call(name:String, varage args:Any?)
62- // 调用并使用方法返回值构建新的EasyReflect实例并返回.
63- var newReflect = reflect.callWithReturn(name:String, varage args:Any?)
64- ```
58+ EasyReflect对字段的操作,不用再去考虑它是否是` 静态的 ` 或者还是` final修饰的 ` 。更不用去操心字段是否是` private ` 不可见的。我们只需要指定字段名即可。这大大增强了便利性!
6559
66- ### 使用ConstructorReflect操作构造器
60+ - 访问指定字段的值:
6761
6862```
69- // 此实例同样包裹两部分数据:
70- // 1. constructor:此实例操作所使用的构造器
71- // 2. 上级对象。
72- class ConstructorReflect(val constructor: Constructor<*>, val upper:EasyReflect)
63+ val value = reflect.getFieldValue(fieldName)
7364```
7465
75- #### 获取实例
66+ - 为指定字段赋值:
7667
7768```
78- // 仍然是首先创建EasyReflect实例
79- val reflect = ...
80- // 1. 读取此类所有的构造器:
81- val list:List<ConstructorReflect> = reflect.getConsturctors()
82- // 2. 根据参数类型匹配指定的构造器
83- val constructor:ConstructorReflect = reflect.getConstructor(varage types:Class<*>)
69+ reflect.setField(fieldName, newValue)
8470```
8571
86- #### 使用
72+ - 获取指定字段的EasyReflect实例进行使用
8773
8874```
89- val constructorReflect = ...
90- // 使用指定构造器创建新的EasyReflect实例
91- val newReflect:EasyReflect = constructorReflect.newInstance(varage args:Any?)
75+ val newReflect = reflect.getField(fieldName).transform()
9276```
9377
94- ### 使用FieldReflect操作变量
78+ ### 5. 方法调用
79+
80+ 与字段操作类似,我们也不用去担心方法的可见性问题。需要的只有` 此方法存在 ` 即可
81+
82+ - 调用指定方法
9583
9684```
97- class FieldReflect(val field:Field, val upper:EasyReflect)
85+ // 调用指定方法,不含参数
86+ reflect.call(methodName)
87+ // 调用指定方法,含指定参数
88+ reflect.call(methodName, arg0, arg1...argN)
9889```
9990
100- #### 获取实例
91+ - 调用指定方法并获取返回值
10192
10293```
103- // 仍然是首先创建EasyReflect实例
104- val reflect = ...
105- // 1. 读取此类所有的成员变量:
106- val fields:List<FieldReflect> = reflect.getFields()
107- // 2. 根据参数类型匹配指定的构造器
108- val field:FieldReflect = reflect.getField(name:String)
94+ val value = reflect.callWithReturn(methodName, arg0, arg1...argN).instance
10995```
11096
111- #### 使用
97+ ### 6. 传参包括可变参数时的调用方式
11298
113- ```
114- val fieldReflect = ...
99+ 可变参数会在编译时。替换为数组的形式。所以使用反射进行可变参数的传参时,需要使用数组的形式对参数进行构建:
115100
116- // 1. 获取此变量的具体值
117- val value = fieldReflect.getValue()
118- // 2. 为此变量设置值
119- fieldReflect.setValue(value:Any?)
120- // 使用此变量的数据创建新的EasyReflect实例提供使用
121- val newReflect:EasyReflect = fieldReflect.transform()
101+ 以下方的两个方法为例:
102+
103+ ```
104+ class Method{
105+ fun onlyVararg(vararg names:String)
106+ fun withVararg(preffix:Int, vararg names:String)
107+ }
122108```
123109
124- ### 使用MethodReflect
110+ 此类中的两个方法均为带有可变参数的方法,在反射环境下。就应该将其中的可变参数看作为是对应的数组进行使用,所以这个时候。我们需要使用如下方式进行参数传递:
125111
126112```
127- class MethodReflect(val method:Method, val upper:EasyReflect)
113+ // 1. 调用只含有可变参数的方法
114+ reflect.call("onlyVararg", arrayOf("Hello", "World"))
115+ // 2. 调用包含其他参数的可变参数方法
116+ reflect.call("withVararg", 1, arrayOf("Hello", "World"))
128117```
129118
130- #### 获取实例
119+ 在这里要特别提醒一下:由于java与kotlin的差异性,在java中调用 ** 只含有可变参数 ** 的方法时。需要对参数进行特殊处理(外层包裹一层 ` Object[] ` ):
131120
132121```
133- // 仍然是首先创建EasyReflect实例
134- val reflect = ...
135- // 1. 读取此类所有的方法
136- val methods:List<MethodReflect> = reflect.getMethods()
137- // 2. 根据指定方法名name与参数类型types匹配对应的方法
138- val method:MethodReflect = reflect.getMethod(name:Stirng, varage types:Class<*>)
122+ EasyReflect reflect = EasyReflect.create(Method.class)
123+ reflect.call("onlyVararg", new Object[]{new String[]{"Hello", "World"}})
139124```
140125
141- #### 使用
126+ 而对于 ` withVararg ` 这种带有非可变参数的方法则不用进行此特殊处理:
142127
143128```
144- val methodReflect = ..
145-
146- // 1. 执行此方法:
147- methodReflect.call(varage args:Any?)
148- // 2. 执行此方法。并将返回值作为数据。创建出新的EasyReflect实例返回
149- val newReflect = methodReflect.callWithReturn(varage args:Any?)
129+ reflect.call("withVararg", 1, new String[]{"Hello", "World"})
150130```
151131
152- ### 使用动态代理进行托管访问
132+ 这是因为在java中。若实参为一个数组时。这个时候将会对此数组进行解构平铺,所以我们需要在外层再单独套一层Object数组。才能对参数类型做到正确匹配。而在kotlin中不存在此类问题。
133+
134+ ### 7. 使用动态代理进行托管
153135
154136当你需要对某个类进行访问,但是又不想通过写死名字的方式去调用时,可以使用此特性:
155137
156- 假设我们有以下的类,需要进行访问:
138+ ** 通过动态代理的方式,创建一个代理类来对反射操作进行托管 **
157139
158- ```
159- class Test private constructor(private val name:String){
160- constructor():this("默认名字")
140+ 还是通过举例来说明:
161141
162- fun invoked(name:String){
163- ...
164- }
165-
166- companion object {
167- @JvmStatic
168- private fun print(message:String) {
169- ...
170- }
171- }
142+ ```
143+ class Test {
144+ private fun function0(name:String)
172145}
173146```
174147
175- 然后我们想通过代理接管的方式来进行方法调用、访问:
148+ 对于上面所举例的类来说。要通过反射调用它的function0方法,那么需要这样写:
149+
150+ ```
151+ val reflect = EasyReflect.create(Test::class.java)
152+ reflect.call("function0", "This is name")
153+ ```
176154
177- - 配置代理接口 :
155+ 而若使用托管的方式。配置先创建代理接口,然后通过代理接口进行方法访问 :
178156
179157```
180- interface Proxy {
181- fun invoked(name:String)// 托管到Test.invoked方法
182- fun print(message:String) // 托管到Test.print方法
183- fun getName():String // 获取成员变量name的值
184- fun get(name:String):String // 获取成员变量name的值
185- fun setName(value:String) // 为变量name赋值为value
186- fun set(name:String, value:String)// 为变量name赋值为value
158+ // 创建代理接口
159+ interface ITestProxy {
160+ fun function0(name:String)
187161}
162+
163+ val reflect = EasyReflect.create(Test::class.java)
164+ // 创建托管代理实例:
165+ val proxy:ITestProxy = reflect.proxy(ITestProxy::class.java)
166+ // 然后直接通过代理类进行操作:
167+ proxy.function0("proxy name")
188168```
189169
190- 注册托管代理并直接使用 :
170+ 当然,如果只能对方法进行托管那这功能就有点太弱逼了。托管方案也同时支持了对变量的操作 :
191171
192172```
173+ class Test {
174+ // 添加一个字段
175+ private val user:User
176+ private fun function0(name:String)
177+ }
178+
179+ // 创建代理接口
180+ interface ITestProxy {
181+ fun function0(name:String)
182+
183+ //==== 对user字段进行赋值
184+ // 方式一:使用setXXX方法进行赋值
185+ fun setUser(user:User)
186+ // 方式二:使用set(name, value)方法进行赋值
187+ fun set(fieldName:String, value:String)
188+
189+ //==== 对user进行取值
190+ // 方式一:使用getXXX方法进行取值
191+ fun getUser()
192+ // 方式二:使用get(name)方法进行取值
193+ fun get(fieldName:String)
194+ }
195+
193196val reflect = EasyReflect.create(Test::class.java)
194- // 注册托管代理
195- val proxy:Proxy = reflect.proxy(Proxy::class.java)
196- // 使用托管代理直接调用执行
197- proxy.invoked("invoked")// 等同调用Test.invoked("invoked")
198- proxy.print("message") // 等同调用Test.print("message")
199- val name = proxy.getName() // 等同调用Test.name
200- ```
197+ // 创建托管代理实例:
198+ val proxy:ITestProxy = reflect.proxy(ITestProxy::class.java)
199+ // 然后直接通过代理类进行操作:
200+ proxy.setUser(user)// 方式一赋值
201+ proxy.set("user", user)// 方式二赋值
202+
203+ proxy.getUser() // 方式一取值
204+ proxy.get("user")// 方式二取值
205+ ```
206+
207+ 从某种程度上来说:代理托管方案会比一般的直接反射操作要繁琐一点。但是带来的便利也是显而易见的:
208+
209+ 首当其冲的优点就是:托管方式写的代码比直接进行反射操作更加优雅~
210+
211+ 其次,在某些使用环境下,比如在团队协作中:可以将对外不方便直接提供实例的通过托管代理实例进行提供。
212+
213+ 最后,贴上proxy方法代码,希望能对理解托管代理方法的执行原理起到一定的帮助:
214+
215+ ```
216+ fun <T> proxy(proxy:Class<T>):T {
217+ @Suppress("UNCHECKED_CAST")
218+ return Proxy.newProxyInstance(proxy.classLoader, arrayOf(proxy), {_, method, args ->
219+ try {
220+ // 优先匹配存在的方法
221+ return@newProxyInstance this@EasyReflect.callWithReturn(method.name, *args).get()
222+ } catch (e:Exception) {
223+ // 不能匹配到已存在的方法,则匹配set/get方法进行字段托管
224+ try {
225+ val methodName = method.name
226+ if (methodName == "get" && args.size == 1 && args[0] is String) {
227+ return@newProxyInstance getFieldValue(args[0] as String)
228+ } else if (methodName == "set" && args.size == 2 && args[0] is String) {
229+ setField(args[0] as String, args[1])
230+ } else if (methodName.startsWith("get") && method.returnType != Void::class.java) {
231+ val name = methodName.substring(3,4).toLowerCase() + methodName.substring(4)
232+ return@newProxyInstance getFieldValue(name)
233+ } else if (methodName.startsWith("set") && args.size == 1) {
234+ val name = methodName.substring(3,4).toLowerCase() + methodName.substring(4)
235+ setField(name, args[0])
236+ }
237+ } catch (e:Exception) {
238+ // ignore
239+ }
240+ // 最后,兼容不存在的方法,提供默认值的返回值。
241+ return@newProxyInstance when (method.returnType.name) {
242+ "int", "byte", "char", "long", "double", "float", "short" -> 0
243+ "boolean" -> false
244+ else -> method.defaultValue
245+ }
246+ }
247+ }) as T
248+ }
249+ ```
0 commit comments