BeanUtils.copyProperties(復制對象屬性方法)


原創:https://liaoyongguang.blog.csdn.net/

 

背景:

在電商項目中所需要的業務非常多,所以我們的業務數據也會有很多種,這個時候就會有什么VO,DTO,PO等等這些,把業務和我們的基礎數據進行分離轉換。但是一直都沒有什么好一點的轉換類。后來用了一下BeanUtils.copyPropertie,和PropertyUtils.copyProperties()的方法,發現其效率非常低。這里也簡單總結了一下他們的用法及原理以及自己實現的轉換類;

用法:

 

BeanUtils.copyProperties("轉換后的類", "要轉換的類");
 PropertyUtils.copyProperties("轉換后的類", "要轉換的類");

List<MsgUserRecord> idLists = userRecordMapper.getTitleId(Id);
if(CollectionUtils.isEmpty(idLists))return null;
for(MsgUserRecord userRecord : idLists) {
MsgUserRecordResp userRecordResp = new MsgUserRecordResp();
BeanUtils.copyProperties(userRecord, userRecordResp);
 

用法其實很簡單,第一個參數是轉換后的類,第二個參數是待轉換的類;我們可以理解成為后轉前;

原理:

其原理是通過JDK自帶的反射機制動態的去get,set,從而去轉換我們的類。但是要注意一點他們所支持的數據類型,還有一個就是假如一個類里面又寫了一個類,例如這種:

  1. public class Name{
  2.  
  3. }
  4. class Name1{
  5.  
  6. }

一般叫做內部類,像這種類進行轉換的時候,是不會成功的。因為在里面進行讀寫校驗的時候不會通過;

 

  1. PropertyDescriptor[] origDescriptors =
  2. getPropertyDescriptors(orig);
  3. for (int i = 0; i < origDescriptors.length; i++) {
  4. String name = origDescriptors[i].getName();
  5. if (isReadable(orig, name) && isWriteable(dest, name)) {
  6. try {
  7. Object value = getSimpleProperty(orig, name);
  8. if (dest instanceof DynaBean) {
  9. ((DynaBean) dest).set(name, value);
  10. } else {
  11. setSimpleProperty(dest, name, value);
  12. }
  13. } catch (NoSuchMethodException e) {
  14. if (log.isDebugEnabled()) {
  15. log.debug( "Error writing to '" + name + "' on class '" + dest.getClass() + "'", e);
  16. }
  17. }
  18. }
  19. }

上圖是JDK的源代碼,我們執行isRead和isWrite的時候並不會通過;

注意差異,

主要的區別在於BeanUtils
提供類型轉換功能,即發現兩個JavaBean的同名屬性為不同類型時,在支持的數據類型范圍內進行轉換,而前者不支持這個功能,但是速度會更快一些。BeanUtils支持的轉換類型如下:
  1. * java.lang.BigDecimal
  2. * java.lang.BigInteger
  3. * boolean and java.lang.Boolean
  4. * byte and java.lang.Byte
  5. * char and java.lang.Character
  6. * java.lang.Class
  7. * double and java.lang.Double
  8. * float and java.lang.Float
  9. * int and java.lang.Integer
  10. * long and java.lang.Long
  11. * short and java.lang.Short
  12. * java.lang.String
  13. * java.sql.Date
  14. * java.sql.Time
  15. * java.sql.Timestamp


閱讀其源碼,發現其內部是使用了裝飾者模式,我發現Java得工具類很喜歡使用這種模式,而且也十分好用;

他們都使用到了BeanutilsBean和PropertyUtilsBean只不過BeanUtils多了一個轉換的功能而已,但是性能上要比

PropertyUtils慢一些,其實兩個都很慢,最好不要使用;

重寫反射轉換:

 

  1. /**
  2. * @param obj 轉換的對象值
  3. * @param clz 類對象
  4. * @return 轉換后的對象
  5. */
  6. public static<T> T transferObject(Object obj,Class clz){
  7. T result = null;
  8. if(obj!=null&&!obj.equals("")){
  9. Method[] methods = obj.getClass().getMethods();
  10. try {
  11. result = (T)clz.newInstance();
  12. } catch (Exception e1) {
  13. return null;
  14. }
  15. Method m;
  16. for(int i=0;i<methods.length;i++){
  17. m = methods[i];
  18. try {
  19. if(m.getName().startsWith("set")){
  20. String fieldName = m.getName().replaceFirst( "set", "");
  21. Method method = result.getClass().getMethod(m.getName(), m.getParameterTypes());
  22. Method getMethod = obj.getClass().getMethod( "get"+fieldName, new Class[]{});
  23. method.invoke(result, getMethod.invoke(obj, new Object[]{}));
  24. }
  25. } catch (Exception e) {
  26. continue;
  27. }
  28. }
  29. }
  30. return result;
  31. }

上面這個方法也是用了Java反射去寫的,但是少了很多校驗以及轉換。所以在100萬條數據的時候,效率是3739毫秒,而使用BeanUtils是5000毫秒左右。兩個效率都不高;

ReflectASM,高性能的反射:

什么是ReflectASM    ReflectASM是一個很小的java類庫,主要是通過asm生產類來實現java反射,執行速度非常快,看了網上很多和反射的對比,覺得ReflectASM比較神奇,很想知道其原理,下面介紹下如何使用及原理;

 

  1. public static void main(String[] args) {
  2. User user = new User();
  3. //使用reflectasm生產User訪問類
  4. MethodAccess access = MethodAccess.get(User.class);
  5. //invoke setName方法name值
  6. access.invoke(user, "setName", "張三");
  7. //invoke getName方法 獲得值
  8. String name = (String)access.invoke(user, "getName", null);
  9. System.out.println(name);
  10. }

原理 
   上面代碼的確實現反射的功能,代碼主要的核心是 MethodAccess.get(User.class); 
看了下源碼,這段代碼主要是通過asm生產一個User的處理類 UserMethodAccess(這個類主要是實現了invoke方法)的ByteCode,然后獲得該對象,通過上面的invoke操作user類。 

ASM反射轉換:

 

  1. private static Map<Class, MethodAccess> methodMap = new HashMap<Class, MethodAccess>();
  2.  
  3. private static Map<String, Integer> methodIndexMap = new HashMap<String, Integer>();
  4.  
  5. private static Map<Class, List<String>> fieldMap = new HashMap<Class, List<String>>();
  6.  
  7. public static void copyProperties(Object desc, Object orgi) {
  8. MethodAccess descMethodAccess = methodMap.get(desc.getClass());
  9. if (descMethodAccess == null) {
  10. descMethodAccess = cache(desc);
  11. }
  12. MethodAccess orgiMethodAccess = methodMap.get(orgi.getClass());
  13. if (orgiMethodAccess == null) {
  14. orgiMethodAccess = cache(orgi);
  15. }
  16.  
  17. List<String> fieldList = fieldMap.get(orgi.getClass());
  18. for (String field : fieldList) {
  19. String getKey = orgi.getClass().getName() + "." + "get" + field;
  20. String setkey = desc.getClass().getName() + "." + "set" + field;
  21. Integer setIndex = methodIndexMap.get(setkey);
  22. if (setIndex != null) {
  23. int getIndex = methodIndexMap.get(getKey);
  24. // 參數一需要反射的對象
  25. // 參數二class.getDeclaredMethods 對應方法的index
  26. // 參數對三象集合
  27. descMethodAccess.invoke(desc, setIndex.intValue(),
  28. orgiMethodAccess.invoke(orgi, getIndex));
  29. }
  30. }
  31. }
  32.  
  33. // 單例模式
  34. private static MethodAccess cache(Object orgi) {
  35. synchronized (orgi.getClass()) {
  36. MethodAccess methodAccess = MethodAccess.get(orgi.getClass());
  37. Field[] fields = orgi.getClass().getDeclaredFields();
  38. List<String> fieldList = new ArrayList<String>(fields.length);
  39. for (Field field : fields) {
  40. if (Modifier.isPrivate(field.getModifiers())
  41. && !Modifier.isStatic(field.getModifiers())) { // 是否是私有的,是否是靜態的
  42. // 非公共私有變量
  43. String fieldName = StringUtils.capitalize(field.getName()); // 獲取屬性名稱
  44. int getIndex = methodAccess.getIndex("get" + fieldName); // 獲取get方法的下標
  45. int setIndex = methodAccess.getIndex("set" + fieldName); // 獲取set方法的下標
  46. methodIndexMap.put(orgi.getClass().getName() + "." + "get"
  47. + fieldName, getIndex); // 將類名get方法名,方法下標注冊到map中
  48. methodIndexMap.put(orgi.getClass().getName() + "." + "set"
  49. + fieldName, setIndex); // 將類名set方法名,方法下標注冊到map中
  50. fieldList.add(fieldName); // 將屬性名稱放入集合里
  51. }
  52. }
  53. fieldMap.put(orgi.getClass(), fieldList); // 將類名,屬性名稱注冊到map中
  54. methodMap.put(orgi.getClass(), methodAccess);
  55. return methodAccess;
  56. }
  57. }

執行1000000條效率80幾毫秒,效率已經沒問題了;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM