
前言
PropertyDescriptor 是位于 java.beans 包下的一个类,其注释对其的解释为: PropertyDescriptor 描述了 Java Bean 的一个属性,我们可以通过一对 getter 和 setter 方法对其进行访问。
我对其产生兴趣是因为在看 Spring 源码的过程中,发现 Spring 会通过 org.springframework.beans.BeanWrapperImpl
对其管理的 bean 进行包装,然后调用它的 org.springframework.beans.PropertyAccessor#setPropertyValues(org.springframework.beans.PropertyValues)
方法对属性进行填充,其中参数 PropertyValues
就包含 PropertyDescriptor 属性。在对属性进行填充时,实际上是先获取 PropertyValues
对象的 WriteMethod,然后调用其 invoke 方法,最终通过反射的方式完成属性设置。
举个栗子
如果是要通过 Spring 对 bean 的管理来重现上边的过程比较的麻烦,所以这里手写一个小栗子来模拟这个过程。这里只有两个类,User 为用户类,我们这里就是要对它的属性进行访问,另外一个就是 App 类,所有的逻辑都在其中实现,代码很简单,我们主要是为了观察 setter 方法被执行的过程。
User 类
1 | public class User { |
App 类
1 | import java.beans.PropertyDescriptor; |
执行结果如下:
1 | User{id='12', userName='tim'} |
其实代码逻辑很简单,我们仅仅关心 user 实例的 userName 字段被修改的过程,可以看到我们的 PropertyDescriptor 对象是通过其构造函数得到的,并且传入了字段信息为 “userName”,所以我们获取到的 PropertyDescriptor 就是 “userName” 这个字段所代表的 Java Bean 的属性, Method m = pd.getWriteMethod();
就是获取的这个字段的 setter 方法。然后我们调用 invoke 方法,这里有两个参数:此方法所在的实例和此方法的参数。因为获取到的是一个 Method 对象,它有一个特殊的字段我们要注意 java.lang.reflect.Method#methodAccessor
,这里我把它的源码贴出来:
1 | package sun.reflect; |
可以看到这个这个接口的唯一一个方法的签名和 Method 类的的 invoke 方法的签名是一模一样的,而且注释中也说了每一个 Method 对象都会包含一个实现了这个接口的对象。通过查看 Method 类的 invoke 方法,可以发现它最终也就是调用了 MethodAccessor 实现类的 invoke 方法。
1 | public Object invoke(Object obj, Object... args) |
MethodAccessor 的实现类有两个 sun.reflect.DelegatingMethodAccessorImpl
和 sun.reflect.NativeMethodAccessorImpl
,通过 idea 的调试功能,可以看到 Method 包含的是 DelegatingMethodAccessorImpl 这个实现类,我们继续查看它对 invoke 方法的实现,很简单,就是直接调用了它所包含的 MethodAccessor
实现类的 invoke 方法,注意这里 MethodAccessor
的实现类变成了 NativeMethodAccessorImpl
,好了那就直接看看 NativeMethodAccessorImpl
的 invoke 方法实现吧:
1 | public Object invoke(Object obj, Object[] args) |
可以看到在 invoke 方法中,是最后调用了 invoke0 方法,而且在 java 中有个特点,就是如果一个方法是以数字 0 结尾,那么通常这个方法就是一个本地方法,这里也不例外,最后 invoke0 就是一个本地方法。本地方法对我们就是一个黑盒,但是我们可以通过其传入的参数猜测一下他的作用。
一共三个参数, 分别是 method obj 和 orgs,通过 idea 的 debug 直视化功能,我们可以很直观的看到三个参数的值分别为 public void User.setUserName(java.lang.String)
,User{id='12', userName='eva'}
,tim
,这不正好就是我们期望调用的方法、参数和对象吗?好了,提前在目标方法中打上断点,再直接下一步,可以看到程序停在了 User 类的 setUsername 方法上了,这样方法的调用过程就分析完毕。
总结
之前在看 Aop 的实现原理时,发现动态代理的过程也会执行上述的代码,但是当时并没有看懂各种类之间是什么关系,这次通过一个比较简单的例子重新走了一个这个过程,大致理清了下通过反射调用的过程。但是对于 Aop 的增强方法的链式调用过程还是很模糊,有时间了再去看看 Aop 实现的过程。