性能對比: beancopier > propertyutils > beanutils. 其中beancopier的性能高出另外兩個100數量級。
業務系統中經常需要兩個對象進行屬性的拷貝,不能否認逐個的對象拷貝是最快速最安全的做法,但是當資料對象的屬性字段數量超過程式員的容忍的程度,代碼是以變得臃腫不堪,使用一些友善的對象拷貝工具類将是很好的選擇。
apache的兩個版本:(反射機制)
org.apache.commons.beanutils.propertyutils.copyproperties(object dest, object orig)
org.apache.commons.beanutils.beanutils.copyproperties(object dest, object orig)
spring版本:(反射機制)
org.springframework.beans.beanutils.copyproperties(object source, object target, class editable, string[] ignoreproperties)
cglib版本:(使用動态代理,效率高)
net.sf.cglib.beans.beancopier.copy(object paramobject1, object paramobject2, converter paramconverter)
都使用靜态類調用,最終轉化虛拟機中兩個單例的工具對象。
public beanutilsbean()
{
this(new convertutilsbean(), new propertyutilsbean());
}
convertutilsbean可以通過convertutils全局自定義注冊。
convertutils.register(new dateconvert(), java.util.date.class);
propertyutilsbean的copyproperties方法實作了拷貝的算法。
1、 動态bean:orig instanceof dynabean:object value = ((dynabean)orig).get(name);然後把value複制到動态bean類
2、 map類型:orig instanceof map:key值逐個拷貝
3、 其他普通類::從beaninfo【每一個對象都有一個緩存的bean資訊,包含屬性字段等】取出name,然後把sourceclass和targetclass逐個拷貝
copier = beancopier.create(source.getclass(), target.getclass(), false);
copier.copy(source, target, null);
create對象過程:産生sourceclass-》targetclass的拷貝代理類,放入jvm中,是以建立的代理類的時候比較耗時。最好保證這個對象的單例模式,可以參照最後一部分的優化方案。
建立過程:源代碼見jdk:net.sf.cglib.beans.beancopier.generator.generateclass(classvisitor)
1、 擷取sourceclass的所有public get 方法-》propertydescriptor[] getters
2、 擷取targetclass 的所有 public set 方法-》propertydescriptor[] setters
3、 周遊setters的每一個屬性,執行4和5
4、 按setters的name生成sourceclass的所有setter方法-》propertydescriptor getter【不符合javabean規範的類将會可能出現空指針異常】
5、 propertydescriptor[] setters-》propertydescriptor setter
6、 将setter和getter名字和類型 配對,生成代理類的拷貝方法。
copy屬性過程:調用生成的代理類,代理類的代碼和手工操作的代碼很類似,效率非常高。
<a href="http://blog.csdn.net/express_wind/article/details/7326359">缺陷預防</a>
陷阱條件
apache- propertyutils
apache- beanutils
spring- beanutils
cglib-
beancopier
是否可以擴充
useconvete功能
no
yes
yes,但比較難用
(sourceobject,targetobject)的順序
逆序
ok
對sourceobject特殊屬性的限制:(date,bigdecimal等)【見備注1】
no,異常出錯
相同屬性名,且類型不比對時候的處理
【見備注2】
異常,拷貝部分屬性,非常危險
ok,并能進行初級轉換,long和integer互轉
異常,拷貝部分屬性
ok,但是該屬性不拷貝
get和set方法不比對的處理
【見備注3】
建立拷貝的時候報錯,無法拷貝任何屬性(當且僅當sourceclass的get方法超過set方法)
對targetobject特殊屬性的限制:(date,bigdecimal等)
原因:datetimeconveter的conveter沒有對null值的處理
public class errorbeanutilobject { //此處省略getter,setter方法
private string name;
private java.util.date date;
public class errorbeanutilstest {
public static void main(string args[]) throws throwable {
errorbeanutilobject from = new errorbeanutilobject();
errorbeanutilobject to = new errorbeanutilobject();
//from.setdate(new java.util.date());
from.setname("tttt");
org.apache.commons.beanutils.beanutils.copyproperties(to, from);//如果from.setdate去掉,此處出現conveter異常
system.out.println(tostringbuilder.reflectiontostring(from));
system.out.println(tostringbuilder.reflectiontostring(to));
}
原因:這兩個工具類不支援同名異類型的比對 !!!【包裝類long和原始資料類型long是可以的】
public class targetclass { //此處省略getter,setter方法
private long num;
private string name;
private long num;
public class errorpropertyutilstest {
public static void main(string args[]) throws illegalaccessexception, invocationtargetexception, nosuchmethodexception {
sourceclass from = new sourceclass();
from.setnum(1);
from.setname("name");
targetclass to = new targetclass();
org.apache.commons.beanutils.propertyutils.copyproperties(to, from); //抛出參數不比對異常
org.springframework.beans.beanutils.copyproperties(from, to);
//抛出參數不比對異常
system.out.println(tostringbuilder.reflectiontostring(from));
system.out.println(tostringbuilder.reflectiontostring(to));
public class errorbeancopiertest {
/**
* 從該用例看出beancopier.create的target.class 的每一個get方法必須有隊形的set方法
* @param args
*/
public static void main(string args[]) {
beancopier copier = beancopier.create(unsatifisedbeancopierobject.class, sourceclass.class,false);
copier = beancopier.create(sourceclass.class, unsatifisedbeancopierobject.class, false); //此處抛出異常建立
class unsatifisedbeancopierobject {
public string getname() {
return name;
}
public void setname(string name) {
this.name = name;
public long getnum() {
return num;
// public void setnum(long num) {
// this.num = num;
// }
<a href="http://blog.csdn.net/express_wind/article/details/7326369">優化方案</a>
public class beanutilsex extends beanutils
public static void copyproperties(object dest, object orig)
{
try
{
beanutils.copyproperties(dest, orig);
} catch (illegalaccessexception ex) {
ex.printstacktrace();
} catch (invocationtargetexception ex) {
}
static
convertutils.register(new dateconvert(), java.util.date.class);
convertutils.register(new dateconvert(), java.sql.date.class);
convertutils.register(new bigdecimalconvert(), bigdecimal.class);
将beancopier做成靜态類,友善拷貝
public class beancopierutils {
public static map<string,beancopier> beancopiermap = new hashmap<string,beancopier>();
public static void copyproperties(object source, object target){
string beankey = generatekey(source.getclass(), target.getclass());
beancopier copier = null;
if(!beancopiermap.containskey(beankey)){
copier = beancopier.create(source.getclass(), target.getclass(), false);
beancopiermap.put(beankey, copier);
}else{
copier = beancopiermap.get(beankey);
}
copier.copy(source, target, null);
}
private static string generatekey(class<?> class1,class<?>class2){
return class1.tostring() + class2.tostring();
}
修複beancopier對set方法強限制的限制
改寫net.sf.cglib.beans.beancopier.generator.generateclass(classvisitor)方法
将133行的
methodinfo write = reflectutils.getmethodinfo(setter.getwritemethod());
預先存一個names2放入
/* 109 */ map names2 = new hashmap();
/* 110 */ for (int i = 0; i < getters.length; ++i) {
/* 111 */ names2.put(setters[i].getname(), getters[i]);
/* */ }
調用這行代碼前判斷查詢下,如果沒有改writemethod則忽略掉該字段的操作,這樣就可以避免異常的發生。
特别說明:尊重作者的勞動成果,轉載請注明出處哦~~~http://blog.yemou.net/article/query/info/tytfjhfascvhzxcytp37