先导知识之JAVA内省前段时间刚入职xxx公司,由于公司内部有一套自己的技术架构,并且很多东西都是自研、封装的。身为小菜鸡的在业余时间抽空看了一下里面的源码,感觉这些人基础挺扎实的哈哈哈哈,这些个基础架构工具包全是自研的,牛逼。一下子激发了我看 BeanUtils 源码的兴趣,想着靠自己的摸索,结合之前研究的JDK动态代理技术,自己也尝试着写一套功能类似的基架组件出来(结合代理技术的以后再写,这玩意有点繁琐)。
不清楚JAVA内省的小伙伴,先来了解一下这方面的知识,何为内省?内省是JAVA提供的一组可以操纵 JavaBean 属性的一组 Api,掌握了这些个 Api 我们也可以写一个自己的 BeanUtils 出来。下面先来介绍一波这些个 Api。
- Introspector.getBeanInfo(o.getClass()) 获取当类的一个BeanInfo对象,和Spring中的 BeanDefinition 对象有点类似,BeanDefinition 是封装了 Bean 信息的一个信息对象,BeanInfo 是封装了 Java 对象一系列 Api 方法的对象。
- BeanInfo.getPropertyDescriptors() 获取 PropertyDescriptor 对象,可以通过此对象获取类字段属性 name、value、get和set方法名字、对字段进行属性填充。等操作
恭喜你已经通过了入职考核,有资格进入 zzh 盗版集团来,我们集团这里的人都是人中翘楚、人中龙凤、国之栋梁、梁之中流砥柱、柱中之无懈可击的存在了。下面列举一个 我司开发的山寨版BeanUtils.copyProperties()软件 给读者参观(),温馨提示:前方500米有宝箱,请大家仔细翻阅。大概流程为:遍历操作对源对象与目标对象进行属性名的匹配,匹配上了,随即进行对应属性名的value值填充。
public class IntrospectionTest {
public static List
效果展示
可以看到数据成功的拷贝到了我们指定的 List 中了
步骤流程总是惊人的相似,获取对象的 PropertyDescriptor ,然后调用内省为我们提供的各种操作 字段 属性的Api,最终达到属性拷贝的目的。
private static void copyProperties(Object source, Object target, @Nullable Class> editable, @Nullable String... ignoreProperties) throws BeansException {
Assert.notNull(source, "Source must not be null");
Assert.notNull(target, "Target must not be null");
//获取目标对象Class
Class> actualEditable = target.getClass();
if (editable != null) {
if (!editable.isInstance(target)) {
throw new IllegalArgumentException("Target class [" + target.getClass().getName() + "] not assignable to Editable class [" + editable.getName() + "]");
}
actualEditable = editable;
}
//获取目标对象的所有 PropertyDescriptor
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
List ignoreList = ignoreProperties != null ? Arrays.asList(ignoreProperties) : null;
PropertyDescriptor[] var7 = targetPds;
int var8 = targetPds.length;
//遍历目标对象的 PropertyDescriptor
for(int var9 = 0; var9 < var8; ++var9) {
PropertyDescriptor targetPd = var7[var9];
//获取目标对象的写方法,通过此方法可以对指定字段赋值
Method writeMethod = targetPd.getWriteMethod();
if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
//获取源对象 PropertyDescriptor
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
if (sourcePd != null) {
//获取源对象的读方法,通过此方法可以获取对应字段的 value值
Method readMethod = sourcePd.getReadMethod();
if (readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
try {
//设置一下方法操作权限,为可操作
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
//获取源对象字段中的 value值
Object value = readMethod.invoke(source);
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
//为目标对象进行属性填充
writeMethod.invoke(target, value);
} catch (Throwable var15) {
throw new FatalBeanException("Could not copy property '" + targetPd.getName() + "' from source to target", var15);
}
}
}
}
}
}
总结
Spring为我们提供的 BeanUtils 工具类其实也就是对 内省 Api中的一些方法的包转而已,思考:内省的实现原理是什么呢?我们是否可以从 Jdk 或者 CgLib动态代理入手,自己写一套 山寨版 内省 Api呢?答案肯定是可以的,以后有时间再去研究一下这个,读者感兴趣的话,也可以尝试着自己写一套内省 Api哦



