1. BeanUtils的应用
调用BeanUtils.populate(object, map)可以将一个Map的按照对应的名值对转载到一个Bean对象中。这里有一个高级一点的用法。代码结构为,Father和Child分别继承自Person,Child具有Grade域而Father有Job和Children域,其中Children为一个数组类型的域。
- Person
import java.util.Date; public class Person implements java.io.Serializable, Cloneable{ public Person() { super(); } private String name; private String age; private Date birthday; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } }
- Father
import java.util.ArrayList; import java.util.List; public class Father extends Person { private List<Child> children = new ArrayList<Child>(); private String job; public String getJob() { return job; } public void setJob(String job) { this.job = job; } public Child getChildren(int index){ if (this.children.size() <= index){ this.children.add(new Child()); } return this.children.get(index); } public Person[] getChildren(){ return (Person[]) children.toArray(); } public void setChildren(int index, Child child) { this.children.add(child); } }
- Child
public class Child extends Person { private String grade; public String getGrade() { return grade; } public void setGrade(String grade) { this.grade = grade; } }
- 类图
下面的这段代码展示了调用BeanUtils.populate使用一个Map填充一个Father对象。比较特别的,在Map的键值中我们使用了children[0].name这样的字符串来说明需要填充Father的children域,它是一个Child数组。其中中括号里面的0表示数组的索引。
- BeanUtilTest
import java.lang.reflect.InvocationTargetException; import java.util.Date; import java.util.HashMap; import java.util.Map; import org.apache.commons.beanutils.BeanUtils; import org.apache.commons.beanutils.ConvertUtils; import org.apache.commons.beanutils.locale.converters.DateLocaleConverter; public class BeanUtilTest { public void testPopulate() throws IllegalAccessException, InvocationTargetException { Map<String, Object> map = new HashMap<String, Object>(); map.put("name", "tan"); map.put("birthday", "1980-6-1"); map.put("children[0].name", "zihui"); map.put("children[0].birthday", "2008-2-13"); map.put("children[0].grade", "G3"); map.put("job", "engineer"); ConvertUtils.register(new DateLocaleConverter(), Date.class); Father f = new Father(); BeanUtils.populate(f, map); System.out.println(f.getName()); System.out.println(f.getJob()); System.out.println(f.getChildren(0).getName()); System.out.println(f.getChildren(0).getGrade()); } public static void main(String[] args) throws IllegalAccessException, InvocationTargetException { BeanUtilTest but = new BeanUtilTest(); but.testPopulate(); } }
- 执行结果
此代码在JDK1.7.0_60的环境中执行结果如下:
tan engineer zihui G3
2. 升级JDK1.8.0_102之后
把jre library升级成JDK1.8.0_102执行此代码出错。错误信息如下:
Exception in thread "main" java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.apache.commons.beanutils.PropertyUtilsBean.invokeMethod(PropertyUtilsBean.java:2116) at org.apache.commons.beanutils.PropertyUtilsBean.getIndexedProperty(PropertyUtilsBean.java:542) at org.apache.commons.beanutils.PropertyUtilsBean.getIndexedProperty(PropertyUtilsBean.java:446) at org.apache.commons.beanutils.PropertyUtilsBean.getNestedProperty(PropertyUtilsBean.java:806) at org.apache.commons.beanutils.PropertyUtilsBean.getProperty(PropertyUtilsBean.java:884) at org.apache.commons.beanutils.BeanUtilsBean.setProperty(BeanUtilsBean.java:894) at org.apache.commons.beanutils.BeanUtilsBean.populate(BeanUtilsBean.java:821) at org.apache.commons.beanutils.BeanUtils.populate(BeanUtils.java:431) at BeanUtilTest.testPopulate(BeanUtilTest.java:27) at BeanUtilTest.main(BeanUtilTest.java:37) Caused by: java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [LPerson; at Father.getChildren(Father.java:27) ... 14 more
3. 寻找错误原因
通过调试jdk 1.7和jdk 1.8,发现直接原因是jdk1.7下PropertyUtilsBean.getIndexedProperty(Object bean,String name, int index)在521行返回,而jdk1.8在542行抛出异常。
- 代码片段17行为源代码521行,38行为源代码542行
PropertyDescriptor descriptor = getPropertyDescriptor(bean, name); if (descriptor == null) { throw new NoSuchMethodException("Unknown property '" + name + "' on bean class '" + bean.getClass() + "'"); } // Call the indexed getter method if there is one if (descriptor instanceof IndexedPropertyDescriptor) { Method readMethod = ((IndexedPropertyDescriptor) descriptor). getIndexedReadMethod(); readMethod = MethodUtils.getAccessibleMethod(bean.getClass(), readMethod); if (readMethod != null) { Object[] subscript = new Object[1]; subscript[0] = new Integer(index); try { return (invokeMethod(readMethod,bean, subscript)); } catch (InvocationTargetException e) { if (e.getTargetException() instanceof IndexOutOfBoundsException) { throw (IndexOutOfBoundsException) e.getTargetException(); } else { throw e; } } } } // Otherwise, the underlying property must be an array Method readMethod = getReadMethod(bean.getClass(), descriptor); if (readMethod == null) { throw new NoSuchMethodException("Property '" + name + "' has no " + "getter method on bean class '" + bean.getClass() + "'"); } // Call the property getter and return the value Object value = invokeMethod(readMethod, bean, EMPTY_OBJECT_ARRAY);
进一步阅读代码,我们可以判断出jdk1.7和jdk1.8对person的children property返回的PropertyDescriptor不同,导致了这段代码出现了异常。jdk1.7返回的是IndexedPropertyDescriptor,而jdk1.8返回的则不是IndexedPropertyDescriptor。
4. 验证错误原因
简化测试代码如下
- PropertyDescriptorTest
import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.util.ArrayList; import java.util.List; public class PropertyDescriptorTest { public static void main(String[] args) throws IntrospectionException { BeanInfo info2 = Introspector.getBeanInfo(Father.class); PropertyDescriptor[] descriptors2 = info2.getPropertyDescriptors(); for (int i = 0; i < descriptors2.length; i++) { System.out.println(descriptors2[i].getClass().getName() + ":" + descriptors2[i].getName()); } } }
- jdk1.7的测试结果
java.beans.PropertyDescriptor:age java.beans.PropertyDescriptor:birthday java.beans.IndexedPropertyDescriptor:children java.beans.PropertyDescriptor:class java.beans.PropertyDescriptor:job java.beans.PropertyDescriptor:name
- jdk1.8的测试结果
java.beans.PropertyDescriptor:age java.beans.PropertyDescriptor:birthday java.beans.PropertyDescriptor:children java.beans.PropertyDescriptor:class java.beans.PropertyDescriptor:job java.beans.PropertyDescriptor:name
以上测试结果证明了我们的猜测。
5.比较jdk1.7和jdk1.8源代码,找出根本原因
java.beans.Introspector类通过getBeanInfo产生了一个BeanInfo来描叙一个java bean,BeanInfo中包含每个域的描叙PropertyDescriptor,由getPropertyDescriptors返回一个PropertyDescriptor数组。
而在初始化BeanInfo的方法Introspector.getBeanInfo(Father.class)中,通过调试,可以看出 PropertyDescriptor是在Introspector的私有方法processPropertyDescriptors中被初始化的。比较jdk1.7和jdk1.8的源代码,可以看出这个私有方法有很大的变动。
进一步调试,我发现影响children的PropertyDescriptor类型被判断成PropertyDescriptor的关键代码是jdk1.8 类Introspector的748到764行的逻辑。代码如下:
if (pd == null) { pd = ipd; } else { Class<?> propType = pd.getPropertyType(); Class<?> ipropType = ipd.getIndexedPropertyType(); if (propType.isArray() && propType.getComponentType() == ipropType) { pd = pd.getClass0().isAssignableFrom(ipd.getClass0()) ? new IndexedPropertyDescriptor(pd, ipd) : new IndexedPropertyDescriptor(ipd, pd); } else if (pd.getClass0().isAssignableFrom(ipd.getClass0())) { pd = pd.getClass0().isAssignableFrom(ipd.getClass0()) ? new PropertyDescriptor(pd, ipd) : new PropertyDescriptor(ipd, pd); } else { pd = ipd; } }
反观jdk1.7的代码,我们可以看出此逻辑为jdk1.8独有的逻辑,初步判读jdk1.8针对IndexedPropertyDescriptor的判断有了一些新的特征。通过调试,发现因为没有满足以下条件,所以children属性被判断成普通的PropertyDescriptor而非我们期望的IndexedPropertyDescriptor。
if (propType.isArray() && propType.getComponentType() == ipropType) {
其中propType.isArray()返回为真,因此我们判断出getChildren方法的返回类型必须一致才能够满足条件。
6.修改
修改Father类的定义。
- new Father代码如下:
import java.util.ArrayList; import java.util.List; public class Father extends Person { private List<Child> children = new ArrayList<Child>(); private String job; public String getJob() { return job; } public void setJob(String job) { this.job = job; } public Child getChildren(int index){ if (this.children.size() <= index){ this.children.add(new Child()); } return this.children.get(index); } //Fix return type, keep it consistance with getChildren(int index) public Child[] getChildren(){ return (Child[]) children.toArray(); } public void setChildren(int index, Child child) { this.children.add(child); } }
在jkd1.8上执行测试方法,结果符合我们的期望:
java.beans.PropertyDescriptor:age java.beans.PropertyDescriptor:birthday java.beans.IndexedPropertyDescriptor:children java.beans.PropertyDescriptor:class java.beans.PropertyDescriptor:job java.beans.PropertyDescriptor:name
相关推荐
mac系统jdk1.8安装包!mac系统jdk1.8安装包!mac系统jdk1.8安装包!mac系统jdk1.8安装包!mac系统jdk1.8安装包!mac系统jdk1.8安装包!mac系统jdk1.8安装包!mac系统jdk1.8安装包!mac系统jdk1.8安装包!mac系统jdk...
再者,JDK1.8引入了默认方法(Default Methods)到接口中,这是一个重大的设计改变。默认方法允许在接口中定义具有实现的方法,这样可以在不破坏已有实现的情况下为接口添加新的功能。这在升级API时尤其有用,避免了...
8. **Map接口的改进**:`Map`接口增加了几个新的方法,如`putIfAbsent()`, `remove()`, `replace()`, 和`compute()`, `merge()`,使得在并发环境中对Map的操作更加安全和便捷。 9. **类型推断的增强**:编译器现在...
JDK 1.8对日期和时间API进行了重大改进,引入了`java.time`包,包含`LocalDate`、`LocalTime`、`LocalDateTime`等类,替代了旧的`java.util.Date`和`java.util.Calendar`,提供了更强大、更易用的时间日期处理功能...
**JDK 1.8 中文API文档**是Java开发者的重要参考资料,它包含了JDK 1.8版本的所有核心类库、接口、方法和异常的详细说明,方便开发者理解和使用。这个文档是基于谷歌翻译的版本,虽然可能存在部分翻译不准确的情况,...
**标题解析:**"JDK1.8 API 中文文档" 这个标题指的是Java Development Kit (JDK) 1.8版本的API(Application Programming Interface)的中文解释文档。API是一系列预先定义的函数、类、接口和枚举,用于帮助程序员...
在接口中,JDK 1.8引入了默认方法,允许在接口中定义带有实现的方法。这使得接口可以在不破坏向后兼容性的情况下添加新的方法。例如,`public interface MyInterface { default void myMethod() { // 实现代码 } }` ...
结合JDK 1.8的API中文文档,开发者可以学习如何在实际项目中应用上述特性,例如使用Lambda简化集合操作,利用Stream API进行复杂的数据处理,或者通过新的日期和时间API来处理日期相关的问题。此外,文档还详细解释...
8. **并行GC的优化**:在垃圾收集方面,JDK1.8对G1(Garbage First)垃圾收集器进行了优化,使其更适合大规模服务端应用,提高了应用程序的响应速度和吞吐量。 9. ** Nashorn JavaScript引擎**:JDK1.8包含了...
提供两种资源方式:(JDK1.8压缩包64位Windows版本)上面JDK1.8压缩包直接下载(解压一下就可以用),想自己下载的下方官网网址自行查找 官网下载地址:https://www.oracle.com/java/technologies/downloads/ JDK...
《JDK1.8中文API手册》是Java开发者的重要参考资料,它详尽地列出了Java Development Kit 1.8版本中的各种类、接口、方法和常量,为开发者提供了全面的编程指南。以下是对其中一些关键知识点的详细介绍: 1. **...
jdk1.8中文.CHM
JDK1.8是Java 8版本的开发工具包,它的发布对Java社区产生了深远影响,引入了许多新的特性和改进。 1. **模块化系统(Project Jigsaw)** JDK1.8首次引入了模块化系统,这是一个重大变革,旨在提高程序的可维护性...
JDK1.8 API 中文 java帮助文档 JDK API java 帮助文档 百度翻译 JDK1.8 API 中文 java帮助文档 Java最新帮助文档 本帮助文非人工翻译。准确性不能保证,请与英文版配合使用
在JDK1.8环境下部署Dubbo Admin是为了确保与 Dubbo 兼容性,因为不同的Dubbo版本可能对JDK有特定的要求。 【描述】中的“dubbo admin jdk1.8的环境的tomcat解压文件,亲测可以用”意味着这个压缩包包含了已经在JDK...
这份"jdk1.8中文文档.zip"包含了完整的JDK 1.8中文版官方文档,对于正在学习或工作中使用Java 1.8的开发者来说,是一份非常宝贵的学习资源。 首先,文档中的"Java SE 8 API"部分详尽地介绍了JDK 1.8提供的所有类库...
1. **IDE集成**:大部分现代Java集成开发环境(如Eclipse、IntelliJ IDEA)都支持JDK 1.8,只需在IDE中配置好JDK路径即可开始开发。 2. **编写Java代码**:利用新特性如Lambda表达式和Stream API,可以编写出更加...
JDK 1.8是Java历史上的一个重要版本,它引入了许多新特性,增强了性能,并且对开发者提供了更多的便利。下面将详细讨论JDK 1.8中的关键知识点。 1. **Lambda表达式**:这是JDK 1.8最重要的更新之一,它简化了处理...
JDK 1.8作为Java生态系统中的一个重要里程碑,不仅引入了诸如Lambda表达式、Stream API等重大创新,还改进了原有的API和工具集,显著提升了开发效率和程序性能。对于Mac OS X用户来说,通过简单的步骤即可安装配置好...
使用JDK1.8 Windows Zip解压缩版,用户只需将压缩包解压到指定目录,设置好环境变量(如JAVA_HOME、PATH),即可在命令行中使用javac进行编译,java命令运行程序。对于开发人员来说,这是一个快速启动Java开发的便捷...