本文為舊站搬過來的內容,原寫於2016-12-17
最近在工作時會研究一下前人寫的程式碼,才發現說有些開源框架提供的工具類別可以拿來用,這裡先寫寫我在專案中比較常看到的StringUtils、CollectionUtils與BeanUtils
Apache Commons
在開始之前來看看Apache Commons,它是Apache的一個專案,提供可重用的、開源的Java程式碼,分成Proper、Sandbox和Dormant三個部分,我們主要在用的是Proper底下的組件,差不多有四十種左右可以自由使用,例如:
組件 | 說明 |
---|---|
BeanUtils | 針對Bean的包裝和各種操作 |
Collections | 對Java的Collection做加強 |
Lang | 讓java.lang套件中的類別增加更多功能 |
StringUtils
StringUtils是Apache Commons Lang當中的類別( org.apache.commons.lang3.StringUtils),所以使用前需要先加入Apache Commons Lang的jar檔,如果用maven的話可以用加入dependency的方式,目前的最新版本是3.5,Apache Commons Lang除了StringUtils以外還有像ArrayUtils、andomStringUtils、Tokenizer、WordUtils之類的工具類別
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
StringUtils類別的方法都是null safe的,也就是說就算遇到傳進來的String是Null,也不會有NullPointerException出現,我比較常用到的方法大概有幾個:
IsEmpty/IsBlank 判斷字串有沒有內容,差別在於字串如果是” “(空格)的話,isEmpty()會回傳false,isBlank()則會回傳true
Trim/Strip 去掉字串前後的空格,Strip除了空格以外,也可以指定其他要去除的字元。trimToEmpty()跟trimToNull()的差別在於前者trim完之後如果字串是null或空字串,則一律回傳空字串,後者則是都回傳null
Equals/Compare 字串間的比較,就算拿來比較的兩個字串都是null也可以比,equals()方法的兩個參數如果都是null會回傳true,而compare()則會回傳0
LeftPad/RightPad 將某個字串的長度增加到某個值,多出來的部分補上特定的字,例如:
String test1 = "測試"; System.out.println(StringUtils.rightPad(test1, 10, "a")); //測試aaaaaaaa
CollectionUtils
CollectionUtils是Apache Commons Collections當中的類別(org.apache.commons.collections4.CollectionUtils),使用前也是要加入jar檔,目前最新版本是4.1
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.1</version>
</dependency>
CollectionUtils我比較常用的就是isEmpty()跟isNotEmpty()這兩個
之前可能要這麼寫:
List<String> testList = null;
System.out.println(testList != null && testList.size() > 0 ); //false
使用CollectionUtils.isNotEmpty():
List<String> testList = null;
System.out.println(CollectionUtils.isNotEmpty(testList)); //false
BeanUtils
BeanUtils是Apache Commons BeanUtils當中的類別(org.apache.commons.beanutils.BeanUtils),目前Apache Commons BeanUtils的最新版本是1.9.3
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.3</version>
</dependency>
不過我所接觸到的專案所使用的BeanUtils是Spring的(org.springframework.beans.BeanUtils),其實除了Apache、Spring以外,還有其他開源框架也有提供類似功能的類別,各具特色,但都有Bean屬性複製的方法,只是實作的方式不盡相同(例:Apache、Spring使用反射機制,cglib使用動態代理…),效能上會有所差別,可以參考Bean复制的几种框架性能比较(Apache BeanUtils、PropertyUtils,Spring BeanUtils,Cglib BeanCopier)這篇文章
假設先定義兩個JavaBean
public class EmpVO implements java.io.Serializable {
private Integer empno;
private String ename;
private String job;
private Date hiredate;
private Double sal;
private Double comm;
private DeptVO deptVO;
//省略getter/setter
}
public class MyEmpVO extends EmpVO implements java.io.Serializable {
private String addr;
private String phone;
//省略getter/setter
}
另外定義一個有同名但型態不同的Java Bean
public class DifferentEmpVO implements java.io.Serializable {
private Integer empno;
private String ename;
private String job;
private String hiredate; //型態不同
private String address;
private String phone;
private Integer sal; //型態不同
//省略getter/setter
}
org.apache.commons.beanutils.BeanUtils
//將orig的屬性複製到dest中
public static void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException
//將beanA的某個屬性值複製到beanB
public static void copyProperty(Object bean, String name, Object value) throws IllegalAccessException, InvocationTargetException
例如:
public static void main(String[] args) {
EmpVO emp1 = new EmpVO();
emp1.setEmpno(1001);
emp1.setEname("Carrie");
emp1.setHiredate(new java.sql.Date(new Date().getTime()));
emp1.setJob("Manager");
emp1.setSal(50000D);
emp1.setComm(200D);
DeptVO dept = new DeptVO();
emp1.setDeptVO(dept);
dept.setDeptno(10);
dept.setDname("業務部");
dept.setLoc("台北市");
MyEmpVO emp2 = new MyEmpVO();
MyEmpVO emp3 = new MyEmpVO();
DifferentEmpVO emp4 = new DifferentEmpVO();
try {
BeanUtils.copyProperties(emp2, emp1);
dept.setLoc("高雄市"); //會修改兩個Bean所參考的物件
System.out.println(emp2);
BeanUtils.copyProperty(emp3, "ename", emp1.getEname()); //只複製ename
System.out.println(emp3);
BeanUtils.copyProperties(emp4, emp1); //有同名但型態不同的屬性
System.out.println(emp4);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
empno=1001,ename=Carrie,job=Manager,hiredate=2016-12-18,comm=200.0,sal=50000.0,deptno=10,dname=業務部,loc=高雄市,address=null,phone=null
empno=null,ename=Carrie,job=null,hiredate=null,comm=null,sal=null,address=null,phone=null
empno=1001,ename=Carrie,job=Manager,hiredate=2016-12-20,sal=50000,address=null,phone=null
- 效能似乎是幾種方式當中最差的一種(可能是它在背後做太多事了,這樣也容易有某些問題發生)
- 當遇到同名屬性但型態不同時會做基本的轉型
- 某些型態需要再額外註冊Converter否則在值為null時會出錯
- 雖然網路上是這樣講,但我一直試不出來,不知道是不是新的版本有做修正
org.apache.commons.beanutils.PropertyUtils
public static void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException
例如:
MyEmpVO emp5 = new MyEmpVO();
DifferentEmpVO emp6 = new DifferentEmpVO();
try {
PropertyUtils.copyProperties(emp5, emp1);
System.out.println(emp5);
PropertyUtils.copyProperties(emp6, emp1); // 出現例外錯誤
System.out.println(emp6);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
empno=1001,ename=Carrie,job=Manager,hiredate=2016-12-20,comm=200.0,sal=50000.0,deptno=10,dname=業務部,loc=台北市,address=null,phone=null
java.lang.IllegalArgumentException: Cannot invoke com.emp.model.DifferentEmpVO.setHiredate on bean class 'class com.emp.model.DifferentEmpVO' - argument type mismatch - had objects of type "java.sql.Date" but expected signature "java.lang.String"
- 當遇到同名屬性但型態不同時會出現例外錯誤(argument type mismatch)
- 沒辦法自定Converter
org.springframework.beans.BeanUtils
//把source的屬性複製給target
public static void copyProperties(Object source, Object target) throws BeansException
//把source的屬性複製給target,但要忽略掉ignoreProperties所列的屬性
public static void copyProperties(Object source, Object target, String... ignoreProperties) throws BeansException
例如: 複製全部屬性
MyEmpVO emp7 = new MyEmpVO();
BeanUtils.copyProperties(emp1, emp7);
System.out.println(emp7);
empno=1001,ename=Carrie,job=Manager,hiredate=2016-12-20,comm=200.0,sal=50000.0,deptno=10,dname=業務部,loc=台北市,address=null,phone=null
忽略部份屬性
MyEmpVO emp8 = new MyEmpVO();
String[] ignoreProperties = {"ename","empno"}; //忽略ename跟empno
BeanUtils.copyProperties(emp1, emp8, ignoreProperties);
System.out.println(emp8);
empno=null,ename=null,job=Manager,hiredate=2016-12-20,comm=200.0,sal=50000.0,deptno=10,dname=業務部,loc=台北市,address=null,phone=null
遇到同名但不同型態的屬性
MyEmpVO1 emp9 = new MyEmpVO1();
BeanUtils.copyProperties(emp1, emp9);
System.out.println(emp9);
org.springframework.beans.FatalBeanException: Could not copy properties from source to target; nested exception is java.lang.IllegalArgumentException: argument type mismatch
- 當遇到同名屬性但型態不同時會出現例外錯誤(argument type mismatch)
參考資料
- Apache Commons 工具集使用简介
- 编写更少量的代码:使用apache commons工具类库
- BeanUtils工具使用细节
- Spring中的BeanUtils与apache commons中的BeanUtils用法
- BeanUtils.copyProperties VS PropertyUtils.copyProperties
- BeanUtils.copyProperties使用笔记
- Spring 中的BeanUtils与apache中的BeanUtils用法与比较
- Spring中BeanUtils.copyProperties方法测试
- Apache BeanUtils & PropertyUtils和spring Beanutils区别
- Spring BeanUtils.copyProperties和apache commons-beanutils