拷貝工具BeanUtilsBean擴充
同僚在使用ES的時候有這麼樣的需求:
- 拷貝對象時,如果源對象的屬性為null,則不拷貝該屬性到目标對象了;
- 源對象的屬性不為null才會将屬性拷貝到目标對象;
- 如果源對象的屬性是自定義的bean,那麼bean内的屬性也按照1、2那樣拷貝。
這時,使用BeanUtils的copyProperties就不滿足要求,需要對該方法進行擴充。
解決上面問題的封裝類CopyProperties
話不多說,上源碼
java CopyProperties.java
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.beanutils.DynaBean;
import org.apache.commons.beanutils.DynaProperty;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* 拷貝對象屬性工具
* 将源對象中不為空的屬性拷貝到目标對象中,當目标對象的屬性值為null時,會先為改屬性建立一個對象,再将源對象的屬性拷貝過去
* 注:不支援通過内部類定義屬性,因為通過反射建立對象有問題
* @author wjy
*/
public class CopyProperties extends BeanUtilsBean {
private static CopyProperties INSTANCE = null;
private static byte[] lock = new byte[];
private CopyProperties(){}
private static void init() {
synchronized (lock) {
if (null == INSTANCE) {
INSTANCE = new CopyProperties();
}
}
}
public static CopyProperties getInstance() {
if (null == INSTANCE) {
init();
}
return INSTANCE;
}
private Log log = LogFactory.getLog(CopyProperties.class);
//定義需要排掉的資料類型
private final List<String> exceptTypeList = Arrays.asList("Boolean", "Byte", "Character", "Short", "Integer", "Long", "Float", "Double", "String");
/**
* 如果源對象的值為null,不對目标對象對應屬性做操作
* @param bean 目标對象
* @param name 屬性名
* @param value 屬性值
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
@Override
public void copyProperty(Object bean, String name, Object value) throws IllegalAccessException, InvocationTargetException {
if (value == null) {
return;
}
super.copyProperty(bean, name, value);
}
/**
* 拷貝屬性方法
* @param dest 目标對象
* @param orig 源對象
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
@Override
public void copyProperties(Object dest, 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) {
DynaProperty origDescriptors[] = ((DynaBean) orig).getDynaClass().getDynaProperties();
for (int i = ; i < origDescriptors.length; i++) {
String name = origDescriptors[i].getName();
if (getPropertyUtils().isWriteable(dest, name)) {
Object value = ((DynaBean) orig).get(name);
copyProperty(dest, name, value);
}
}
} else if (orig instanceof Map) {
Iterator names = ((Map) orig).keySet().iterator();
while (names.hasNext()) {
String name = (String) names.next();
if (getPropertyUtils().isWriteable(dest, name)) {
Object value = ((Map) orig).get(name);
copyProperty(dest, name, value);
}
}
} else /* if (orig is a standard JavaBean) */ {
//修改了目标對象為JavaBean類型時,拷貝屬性的方法
PropertyDescriptor origDescriptors[] = getPropertyUtils().getPropertyDescriptors(orig);
for (int i = ; i < origDescriptors.length; i++) {
String name = origDescriptors[i].getName();//對象的屬性名稱
String type = origDescriptors[i].getPropertyType().getSimpleName();//對象的屬性類型
if ("class".equals(name)) {
continue; // No point in trying to set an object's class
}
if (!exceptTypeList.contains(type)) {
//過濾出沒有被排掉的對象,也就是要處理的自定義對象
//System.out.println(type + " is not base data type");
Object deepDest = null;
try {
deepDest = getPropertyUtils().getProperty(dest, name);
}catch (NoSuchMethodException e){
; // Should not happen
}
//如果目标對象中的屬性是null,需要為該屬性new一個對象
if(deepDest == null){
Object obj = null;
try {
Class classType = dest.getClass().getField(name).getType();
obj = classType.newInstance();
}catch (NoSuchFieldException e){
; // create object fail
}catch (InstantiationException e){
; // create object fail
}
//反射建立對象失敗,後面拷貝對象會報錯
if(obj==null){
continue;
}
//将new的對象賦給dest對象
origDescriptors[i].getWriteMethod().invoke(dest,obj);
deepDest = obj;
}
//擷取源中的深層對象
Object deepOrig = origDescriptors[i].getReadMethod().invoke(orig);
if(deepOrig == null){
System.out.println("No deep origin bean specified");
continue;
}
//遞歸的将源的深層對象賦給目标深層對象
copyProperties(deepDest,deepOrig);
continue;
}
if (getPropertyUtils().isReadable(orig, name) && getPropertyUtils().isWriteable(dest, name)) {
try {
Object value = getPropertyUtils().getSimpleProperty(orig, name);
//将值賦給目标對象名為name的屬性
copyProperty(dest, name, value);
} catch (NoSuchMethodException e) {
; // Should not happen
}
}
}
}
}
}
Man.java
/**
* 男人:姓名、年齡、性别、家庭
* @author wjy
*/
public class Man {
public String name;
public Integer age;
public Boolean sex;
public Family family;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Boolean getSex() {
return sex;
}
public void setSex(Boolean sex) {
this.sex = sex;
}
public Family getFamily() {
return family;
}
public void setFamily(Family family) {
this.family = family;
}
@Override
public String toString() {
return "Man{" +
"name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
", family=" + family +
'}';
}
}
Family.java
/**
* 家庭:老婆、孩子
* @author wjy
*/
public class Family {
public Family(){}
public String wife;
public String child;
public String getWife() {
return wife;
}
public void setWife(String wife) {
this.wife = wife;
}
public String getChild() {
return child;
}
public void setChild(String child) {
this.child = child;
}
@Override
public String toString() {
return "Family{" +
"wife='" + wife + '\'' +
", child='" + child + '\'' +
'}';
}
}
CopyTest.java
/**
* 測試類
* @author wjy
*/
public class CopyTest {
public static void main(String[] args){
Man man = new Man();
man.setName("Rick");
man.setSex(true);
Family fam = new Family();
fam.setWife("Ann");
man.setFamily(fam);//下文會注釋掉的代碼段1.
Man man1 = new Man();
man1.setName("Frank");
man1.setAge();
Family fam1 = new Family();
fam1.setWife("Carry");
fam1.setChild("Wade");
man1.setFamily(fam1);//下文會注釋掉的代碼段2.
System.out.println("origin man:"+man);
System.out.println("dest man1:"+man1);
CopyProperties cp = CopyProperties.getInstance();
try{
cp.copyProperties(man1,man);
}catch (Exception e){
e.printStackTrace();
}
System.out.println("copy man to man1:"+man1);
}
}
運作結果
- 當代碼1跟代碼2都不注釋時,會将man中非null的屬性指派到man1對應的屬性。
-
當隻有代碼1注釋時,會将man中非null的屬性指派到man1對應的屬性。
由于family屬性是null,也是以不會對man1中family的屬性進行修改。
-
當隻有代碼2注釋時,會将man中非null屬性指派到man1對應的屬性。
由于man1的family屬性是null,是以會先為man1建立一個family對象,再将man的family的非空屬性指派給man1的family的對應屬性。
-
當代碼1跟代碼2都注釋掉時,會将man中非null的屬性複制到man1對應的屬性。
但是man、man1的family屬性都是null,是以man1的family隻會建立一個family對象,但是不會被man的family屬性覆寫掉。
如果有寫的不好的地方歡迎拍磚
源碼位址:https://github.com/weijiayou/JavaUtil_CopyProperties