【Java 代码实例 14】BeanUtils用法详解,附源码分析
目录
一、org.apache.commons.beanutils.BeanUtils简介
BeanUtils
是Apache Commons
组件的成员之一,主要用于简化JavaBean
封装数据的操作。
简化反射封装参数的步骤,给对象封装参数,好处是BeanUtils
给对象封装参数的时候会进行类型自动转换。
二、使用的前置条件
- 类//必须使用
public
修饰 - 提供无参数的构造器
- 提供
getter
和setter
方法访问属性
三、添加pom
<dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.9.4</version></dependency>
四、org.apache.commons.beanutils.BeanUtils代码实例
1、为属性赋值
private static void setUserProperty() throws Exception { Worker worker = new Worker(); BeanUtils.setProperty(worker, "id", "1"); BeanUtils.setProperty(worker, "name", "哪吒"); BeanUtils.setProperty(worker, "age", "28"); System.out.println("id:"+ BeanUtils.getProperty(worker, "id")); System.out.println(worker);}
2、拷贝对象,为对象赋值
private static void cloneBean() throws Exception { Worker worker1 = new Worker(3,"哪吒",18); // 拷贝对象 Object object = BeanUtils.cloneBean(worker1); Worker worker2 = (Worker)object; System.out.println(worker2);//Worker{id=3, name='哪吒', age=18} System.out.println("worker1==worker2 : "+(worker1==worker2));//false // 拷贝对象属性 Worker worker3 = new Worker(); BeanUtils.copyProperties(worker3,worker1); System.out.println(worker3);//Worker{id=3, name='哪吒', age=18} System.out.println("worker1==worker3 : "+(worker1==worker3));//false // 为属性赋值 Worker worker4 = new Worker(); BeanUtils.copyProperty(worker4,"age",30); System.out.println(worker4);//Worker{id=0, name='null', age=30}}
3、map转bean
private static void mapToBean() throws Exception { Map<String, Object> map = new HashMap<String, Object>(); map.put("id",2); map.put("name","哪吒"); map.put("age",29); Worker user = new Worker(); BeanUtils.populate(user,map); System.out.println(user);}
4、bean转map
private static void beanToMap() throws Exception { Worker worker = new Worker(4,"哪吒编程",18); Map<String, String> map = BeanUtils.describe(worker); System.out.println(map);//{name=哪吒, id=4, age=18} // 为什么返回值是一个数组呢? String[] names = BeanUtils.getArrayProperty(worker, "name"); System.out.println(names[0]);}
五、Apache的BeanUtils与Spring的BeanUtils
默认情况下,使用org.apache.commons.beanutils.BeanUtils
对复杂对象是进行的浅拷贝,但是由于Apache
下的BeanUtils
对象拷贝性能太差,不建议使用,这在阿里巴巴Java开发规约插件上也明确指出:
Ali-Check | 避免用Apache Beanutils进行属性的copy。
到这里你可能会疑惑,为什么会出现拷贝性能差的情况呢?
因为在对对象进行拷贝的时候添加很多的校验,比如像类型转换,甚至还校验了对象所属类的访问权限,可以说校验是相当的复杂,这也就是造成性能差的根本原因,我们看下它的具体代码实现:
public void copyProperties(final Object dest, final Object orig) throws IllegalAccessException, InvocationTargetException { // Validate existence of the specified beans if (dest == null) { throw new IllegalArgumentException ("No destination bean specified"); } if (orig == null) { throw new IllegalArgumentException("No origin bean specified"); } if (log.isDebugEnabled()) { log.debug("BeanUtils.copyProperties(" + dest + ", " + orig + ")"); } // Copy the properties, converting as necessary if (orig instanceof DynaBean) { final DynaProperty[] origDescriptors = ((DynaBean) orig).getDynaClass().getDynaProperties(); for (DynaProperty origDescriptor : origDescriptors) { final String name = origDescriptor.getName(); // Need to check isReadable() for WrapDynaBean // (see Jira issue# BEANUTILS-61) if (getPropertyUtils().isReadable(orig, name) && getPropertyUtils().isWriteable(dest, name)) { final Object value = ((DynaBean) orig).get(name); copyProperty(dest, name, value); } } } else if (orig instanceof Map) { @SuppressWarnings("unchecked") final // Map properties are always of type Map<String, Object> propMap = (Map<String, Object>) orig; for (final Map.Entry<String, Object> entry : propMap.entrySet()) { final String name = entry.getKey(); if (getPropertyUtils().isWriteable(dest, name)) { copyProperty(dest, name, entry.getValue()); } } } else /* if (orig is a standard JavaBean) */ { final PropertyDescriptor[] origDescriptors = getPropertyUtils().getPropertyDescriptors(orig); for (PropertyDescriptor origDescriptor : origDescriptors) { final String name = origDescriptor.getName(); if ("class".equals(name)) { continue; // No point in trying to set an object's class } if (getPropertyUtils().isReadable(orig, name) && getPropertyUtils().isWriteable(dest, name)) { try { final Object value =getPropertyUtils().getSimpleProperty(orig, name); copyProperty(dest, name, value); } catch (final NoSuchMethodException e) { // Should not happen } } } } }
而org.springframework.beans.BeanUtils
是使用 copyProperties
方法进行拷贝,不过实现的方式相对于前者要来得简单得多了,可以说是非常的简单,就是根据两个对象属性的名字进行匹配,做简单的 get/set
,仅检查属性的可访问性。具体实现如下:
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<?> 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[] targetPds = getPropertyDescriptors(actualEditable);List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);for (PropertyDescriptor targetPd : targetPds) {Method writeMethod = targetPd.getWriteMethod();if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());if (sourcePd != null) {Method readMethod = sourcePd.getReadMethod();if (readMethod != null &&ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {try {if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {readMethod.setAccessible(true);}Object value = readMethod.invoke(source);if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {writeMethod.setAccessible(true);}writeMethod.invoke(target, value);}catch (Throwable ex) {throw new FatalBeanException("Could not copy property '" + targetPd.getName() + "' from source to target", ex);}}}}}}
从上面的实现源码可以看到:成员变量赋值是基于目标对象的成员列表,并且会跳过ignore
以及在源对象中不存在的属性,所以这个方法是安全的,不会因为两个对象之间的结构差异导致错误,但是必须保证同名的两个成员变量类型相同。
以上简要的分析两种BeanUtils
的具体实现以及性能相关的分析,得出结论:Apache下的BeanUtils由于各种繁琐的校验以及可访问性的校验等等,导致性能较差,故实际开发中不建议使用,可以使用 Spring的BeanUtils。
六、org.springframework.beans.BeanUtils代码实例
1、实例化
package com.guor.beanutil.spring;import com.guor.beanutil.Worker;import org.junit.Test;import org.springframework.beans.BeanUtils;public class SpringBeanUtilsTest { @Test public void testInstantiate() throws Exception{ //通过无参数构造函数实例化 Object obj = BeanUtils.instantiateClass(Worker.class); if(obj instanceof Worker) { System.out.println(((Worker) obj).getNezha());//哪吒 } //通过有参数构造函数实例化 Object obj2 = BeanUtils.instantiateClass(Worker.class.getDeclaredConstructor(String.class), "哪吒编程"); if(obj2 instanceof Worker) { System.out.println(((Worker) obj2).getNezha());//哪吒编程 } }}
2、查找方法
(1)无参调用
@Testpublic void testFindDeclaredMethod() throws Exception { Worker worker = new Worker(); worker.setId(8); worker.setName("哪吒"); Method method = BeanUtils.findDeclaredMethod(Worker.class, "getName"); Object name = method.invoke(worker); System.out.println(name);//哪吒}
(2)有参调用
@Testpublic void testPropertyDescriptor2() throws Exception { Worker worker = new Worker(); worker.setId(10); worker.setName("哪吒"); Method method = BeanUtils.findMethod(Worker.class, "sayMsg",String.class,String.class); method.invoke(worker,"哪吒","我很帅");//哪吒 say :我很帅}
(3)如果找不到方法
3、查找属性
@Testpublic void testPropertyDescriptor() throws Exception { Worker worker = new Worker(); worker.setId(10); worker.setName("哪吒"); //获取属性 PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(Worker.class, "name"); //获取属性的get方法 Method getMethod = pd.getReadMethod(); Object name = getMethod.invoke(worker); System.out.println(name);//哪吒 //获取属性的get方法 Method setMethod = pd.getWriteMethod(); setMethod.invoke(worker, "哪吒编程"); System.out.println(worker.getName());//哪吒编程}
七、更多的Java代码实例
【Java 代码实例 4】javacompiler编译多java文件
【Java 代码实例 6】FileUtils、StringUtil、CollectionUtils、ArrayUtils(附代码示例)
【Java 代码实例 7】jsoup解析html
【Java 代码实例 8】qrcode生成二维码
【Java 代码实例 9】Java通过Process执行C# exe程序
【Java 代码实例 13】Java操作pdf的工具类itext
如果您有什么Java方面的需求,可以写在评论区,哪吒会一一进行回复,力争做到《你想知道的Java知识,哪吒都有》,加油!