常用的工具类之 Apache Commons BeanUtils 简单使用与部分原理解析

整理了开发中常用的 JavaBean 处理工具类,包括 Beanutils、PropertyUtils、CollectionUtils、ConvertUtils。近期使用中发现 BeanUtils.populate 方法存在 bug,实体类属性与 Map 中 key 值一模一样,但是会有一些属性拷贝完未null,而且 debug 源码未果,在这里推荐大家一个集成程度更高的 Util 工具类 hutool

  • BeanUtils:主要提供了对于 JavaBean 进行各种操作,比如对象,属性复制等等,自动转换数据类型。
  • PropertyUtils:用处大致与 BeanUtils 相同,但是如果类型不能自动转换会抛异常。
  • CollectionUtils :集合处理类,集合类型转化,取交集、并集等方法。
  • ConvertUtils:数据类型转换类,BeanUtils 的自动类型转换就要到了它。

BeanUtils

导入依赖包:org.apache.commons-beanutils.jar、org.apache.commons-collections.jar、org.apache.commons-logging.jar。

新建 Maven 工程,在 pom.xml 中加入:

    <!-- https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils -->
    <dependency>
      <groupId>commons-beanutils</groupId>
      <artifactId>commons-beanutils</artifactId>
      <version>1.9.3</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/commons-collections/commons-collections -->
    <dependency>
      <groupId>commons-collections</groupId>
      <artifactId>commons-collections</artifactId>
      <version>3.2.1</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.2</version>
    </dependency>

常用方法:

方法说明
cloneBean(Object bean)对象的克隆
copyProperties(Object dest, Object orig)对象的拷贝
copyProperty(Object bean, String name, Object value)拷贝指定的属性值到指定的bean
setProperty(Object bean, String name, Object value)设置指定属性的值
populate(Object bean, Map<String,? extends Object> properties)将map数据拷贝到javabean中(map的key要与javabean的属性名称一致)

处理参数是 map 类型的情况,例如在 jdbc 中获取的结果集使用 hashmap 存储后如何转为其他对象(Servlet 简单应用),使用 populate 方法。

举个例子:

public class App
{
    public static void main( String[] args )
    {
        Dog dog = new Dog();
        Map<String, Object> map = new HashMap<>();
        map.put("age",2);
        map.put("name","huahua");
        try {
            BeanUtils.populate(dog,map);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } finally {
        }
        System.out.println("dog:"+dog);//dog:Dog{name='huahua', age=2}

    }


}

//Dog 类:
public class Dog {
    private String name;
    private int age;

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

下面用一下其他几个方法举几个例子。


// Animal 类
public class Animal {
    private String name;
    private String type;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Animal{" +
                "name='" + name + '\'' +
                ", type='" + type + '\'' +
                ", age=" + age +
                '}';
    }
}

//测试类
public class App
{
    public static void main( String[] args ) throws IllegalAccessException,InvocationTargetException
    {
        // copyProperties 方法
        Animal animal = new Animal();
        animal.setName("mimi");
        animal.setType("dog");
        animal.setAge(2);
        Dog dog = new Dog();
        BeanUtils.copyProperties(dog,animal);
        System.out.println("将 animal 值赋给 dog:");
        System.out.println(dog);
        //如果被copy对象缺少目标对象中的某些属性,那么目标对象中的对应值为 null
        Animal animal1 = new Animal();
        BeanUtils.copyProperties(animal1,dog);
        System.out.println("将 dog 值赋给 animal1 对象:");
        System.out.println(animal1);

        // copyProperty 方法
        animal.setName("huahua");
        BeanUtils.copyProperty(dog,"name",animal.getName());
        System.out.println("改变 animal name属性,并赋给 dog:");
        System.out.println(dog);

        // setProperty 方法
        BeanUtils.setProperty(dog,"age",3);
        System.out.println("给 dog 的 age 属性赋值:");
        System.out.println(dog);

        // cloneBean 方法
        try {
            Animal animal2 = (Animal)BeanUtils.cloneBean(animal1);
            System.out.println("通过 animal1 克隆 animal2 对象:");
            System.out.println(animal2);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }


}

输出结果:

将 animal 值赋给 dog:
Dog{name='mimi', age=2}
将 dog 值赋给 animal1 对象:
Animal{name='mimi', type='null', age=2}
改变 animal name属性,并赋给 dog:
Dog{name='huahua', age=2}
给 dog 的 age 属性赋值:
Dog{name='huahua', age=3}
通过 animal1 克隆 animal2 对象:
Animal{name='mimi', type='null', age=2}

顺带提一下 PropertyUtils

BeanUtils与PropertyUtils这两个类几乎有一摸一样的功能,唯一的区别是:BeanUtils在对Bean赋值是会进行类型转化。

举例来说也就是在copyProperty时只要属性名相同,就算类型不同,BeanUtils也可以进行copy;而PropertyBean则可能会报错!!当然2个Bean之间的同名属性的类型必须是可以转化的,否则用BeanUtils一样会报错。

若实现了org.apache.commons.beanutils.Converter接口则可以自定义类型之间的转化。由于不做类型转化,用PropertyUtils在速度上会有很大提高!

CollectionUtils

集合处理工具类。

举几个例子:

        String[] arrA = new String[]{"1", "2", "3"};
        String[] arrB = new String[]{"1", "a", "b"};
        List<String> listA = Arrays.asList(arrA);
        List<String> listB = Arrays.asList(arrB);

        // 判断集合是否为 空
        System.out.println(CollectionUtils.isEmpty(listA));// false
        System.out.println(CollectionUtils.isEmpty(listB));// false

        // 判断集合是否为 不为空
        System.out.println(CollectionUtils.isNotEmpty(listA));// true
        System.out.println(CollectionUtils.isNotEmpty(listB));// true

        // 两个集合的比较
        System.out.println(CollectionUtils.isEqualCollection(listA, listB));// false

        // 集合的操作
        // 取并集
        System.out.println(CollectionUtils.union(listA, listB));// [1, a, 2, b, 3]
        // 取交集
        System.out.println(CollectionUtils.intersection(listA, listB));// [1]
        // 取交集的补集
        System.out.println(CollectionUtils.disjunction(listA, listB));// [a, 2, b, 3]
        // 取集合相减
        System.out.println(CollectionUtils.subtract(listA, listB));// [2, 3]
        System.out.println(CollectionUtils.subtract(listB, listA));// [a, b]

        List<String> listC = new ArrayList<>();
        listC.add("1");
        listC.add("2");
        listC.add("3");
        String[] arrC = {"4", "5", "6"};
        // 向集合中添加值
        CollectionUtils.addAll(listC, arrC);
        System.out.println(listC);// [1, 2, 3, 4, 5, 6]

ConvertUtils

类型转换工具类,有了这个就不用为记各种数据类型互转的方法而烦恼了。

使用的主要方法:

convert(Object value, Class<?> targetType)//将给定的value转换成指定的Class类型

举个例子:

    public static void main( String[] args ) throws IllegalAccessException,InvocationTargetException
    {
        String str = "10";
        Object obj = ConvertUtils.convert(str, BigDecimal.class);
        System.out.println(obj.getClass());//class java.math.BigDecimal
        Object obj2 = ConvertUtils.convert("100",Integer.class);
        System.out.println(obj2.getClass());//class java.lang.Integer
    }

通过查看 BeanUtils 的源码,发现 BeanUtils 里的自动类型转换其实就是使用了 ConvertUtils,根本上都用到了 ConvertUtilsBean 对象。

查看 ConvertUtils.convert(String str,Class class) 的源码:

    /**
     * <p>Convert the specified value to an object of the specified class (if
     * possible).  Otherwise, return a String representation of the value.</p>
     *
     * <p>For more details see <code>ConvertUtilsBean</code>.</p>
     *
     * @param value Value to be converted (may be null)
     * @param clazz Java class to be converted to (must not be null)
     * @return The converted value
     *
     * @see ConvertUtilsBean#convert(String, Class)
     */
    public static Object convert(final String value, final Class<?> clazz) {
        return ConvertUtilsBean.getInstance().convert(value, clazz);
    }

查看 BeanUtils.copyProperty() 方法的源码:

BeanUtilsBean.java 中的 copyProperty 方法,判断完非空并且获取完目标属性类型后对类型进行转化并存储:

        // Convert the specified value to the required type and store it
        if (index >= 0) {                    // Destination must be indexed
            value = convertForCopy(value, type.getComponentType());
            try {
                getPropertyUtils().setIndexedProperty(target, propName,
                                                 index, value);
            } catch (final NoSuchMethodException e) {
                throw new InvocationTargetException
                    (e, "Cannot set " + propName);
            }

进入 convertForCopy 发现调用了类成员方法 convert ,再看 convert 通过 getConvertUtils 获取了 Converter 对象:


    //顶部声明成员为 ConvertUtilsBean
    private final ConvertUtilsBean convertUtilsBean;

    //实际 convert 方法
    protected Object convert(final Object value, final Class<?> type) {
        final Converter converter = getConvertUtils().lookup(type);
        if (converter != null) {
            log.trace("        USING CONVERTER " + converter);
            return converter.convert(type, value);
        } else {
            return value;
        }
    }

    //获取 ConvertUtilsBean 对象
    public ConvertUtilsBean getConvertUtils() {
        return convertUtilsBean;
    }

这些工具类已经可以满足一般应用开发需要,当然还可以再封装定制。(大佬造好的轮子真的好用)

  • 9
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值