深入理解Java對象復制
一、圖示
二、MapStruct
pom文件
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.16</version> </dependency> <!-- https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils --> <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.9.4</version> </dependency> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>1.2.0.Final</version> </dependency> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-jdk8</artifactId> <version>1.2.0.Final</version> </dependency> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>1.2.0.Final</version> </dependency> <!-- dozer使用時需要配置xml 文件,不推薦使用性能和 BeanUtils 差不多,使用過程可以參考 https://www.jianshu.com/p/bf8f0e8aee23--> <!-- https://mvnrepository.com/artifact/net.sf.dozer/dozer --> <!--<dependency> <groupId>net.sf.dozer</groupId> <artifactId>dozer</artifactId> <version>5.5.1</version> </dependency>-->
下載插件
插件的作用是為瞭在本地測試的時候,生成 接口的 impl 文件(生成的文件存在與target包裡面)
如果是生產環境的話,和Lombok操作一樣,需要在pom文件添加 mapStruct 插件依賴
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.6.1</version> <configuration> <source>1.8</source> <target>1.8</target> <showWarnings>true</showWarnings> <annotationProcessorPaths> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.16</version> </path> <path> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>1.2.0.Final</version> </path> </annotationProcessorPaths> </configuration> </plugin> </plugins> </build>
代碼
import com.baomidou.mybatisplus.annotation.TableName; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; /** * Created by yangLongFei on 2021/5/11 10:44 * Version: $ */ @Data @AllArgsConstructor @NoArgsConstructor @TableName public class Student { private Integer id; private String name; private String age; private String phone; private String address; }
import lombok.Data; import java.io.Serializable; /** * Created by yangLongFei on 2021/5/11 10:51 * Version: $ */ @Data public class StudentDTO implements Serializable { private static final long serialVersionUID = 735190899850778343L; private Integer id; private String name; private String age; private String phone; private String address; }
import lombok.Data; import java.io.Serializable; /** * Created by yangLongFei on 2021/5/11 16:59 * Version: $ */ @Data public class StudentVO implements Serializable { private static final long serialVersionUID = 2059190505074790405L; private Integer pk; private String userName; private String userAge; private String userPhone; private String userAddress; }
接口
import com.sys.yang.dto.StudentDTO; import com.sys.yang.entity.Student; import com.sys.yang.vo.StudentVO; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.Mappings; import org.mapstruct.factory.Mappers; @Mapper public interface ConverterStudent { ConverterStudent INSTANCE = Mappers.getMapper(ConverterStudent.class); @Mappings({ @Mapping(source = "name", target = "name"), @Mapping(source = "age", target = "age") }) StudentDTO entityToDTO(Student student); @Mappings({ @Mapping(source = "id", target = "pk"), @Mapping(source = "name", target = "userName"), @Mapping(source = "age", target = "userAge"), @Mapping(source = "phone", target = "userPhone"), @Mapping(source = "address", target = "userAddress") }) StudentVO dtoToVo(StudentDTO studentDTO); }
測試類
import com.sys.yang.dto.StudentDTO; import com.sys.yang.entity.Student; import com.sys.yang.vo.StudentVO; import org.junit.Test; import org.springframework.beans.BeanUtils; import java.lang.reflect.Method; /** * 對象轉換,映射 * 方式1:效率最高 get set 方法 * 方式2:Common包 BeanUtils.copyProperties 反射的方式進行 * 方式3:mapstruct 推薦使用,操作不復雜,效率和 get set 方式相差不大 * * <p> * Created by yangLongFei on 2021/5/11 10:43 * Version: $ */ public class AToB { /** * set get 的時候使用 * 生成 對象的set方法 */ public static void main(String[] args) { Class<StudentDTO> clazz = StudentDTO.class; Method[] fields = clazz.getDeclaredMethods(); for (Method field: fields) { String name = field.getName(); if(!name.startsWith("is") && !name.startsWith("get")){ System.out.println("entity." + name + "()"); } } } /** * 測試方法 */ @Test public void test1() { Student student = new Student(1,"zhagnsan","18","110112113114","diqiu"); System.out.println(student.toString()); StudentDTO studentDTO1 = new StudentDTO(); BeanUtils.copyProperties(student,studentDTO1); System.out.println("BeanUtils: "+ studentDTO1.toString()); StudentDTO studentDTO2 = ConverterStudent.INSTANCE.entityToDTO(student); System.out.println("mapstruct: entityToDTO " + studentDTO2.toString()); StudentVO studentVO = ConverterStudent.INSTANCE.dtoToVo(studentDTO2); System.out.println("mapStruct: dtoToVo "+ studentVO); } }
生成的接口文件
三、framework cglib
要轉換的對象的,字段名稱 要和 原對象的字段名稱一致,否則賦值會失敗,可以手動 convert 方法,但是,實現後所有的轉換內容都會走 convert 方法
代碼
import lombok.Data; import java.io.Serializable; /** * Created by yangLongFei on 2021/5/11 16:59 * Version: $ */ @Data public class StudentVO implements Serializable { private static final long serialVersionUID = 2059190505074790405L; private Integer pk; private String userName; private String userAge; private String userPhone; private String userAddress; // framework cglib 使用到的內容 private String id; private String name; private Integer age; private String phone; private String address; }
convert 實現類
import org.springframework.cglib.core.Converter; /** * Created by yangLongFei on 2021/5/11 19:53 * Version: $ */ public class ConvertStudentDtoToVo implements Converter { /** * ⭐️⭐️⭐️⭐️⭐️ 要轉換的屬性名稱,相同的情況下,才會走該方法 * @param o 原對象屬性值,value * @param aClass 目標對象屬性 類型,class java.lang.String * @param o1 目標對象屬性set方法,setAddress * @return */ @Override public Object convert(Object o, Class aClass, Object o1) { if (o.getClass().equals(aClass)) { return o; } else { if (o instanceof Integer) { return String.valueOf(o); } if (String.valueOf(o1).contains("Age")) { return Integer.valueOf(o.toString()); } return o; } } }
測試方法
@Test public void test2() { Student student = new Student(1,"zhagnsan","18","110112113114","diqiu"); // false 表示不使用 轉換器, BeanCopier entityToDto = BeanCopier.create(Student.class, StudentDTO.class, false); StudentDTO studentDTO3 = new StudentDTO(); // null 表示,不指定轉換器,要使用轉換器的化,需要實現 Converter 接口 // 屬性名稱之間不能指定映射關系,當屬性名稱不同的時候賦值操作會失敗 entityToDto.copy(student, studentDTO3, null); System.out.println("cglib :entityToDTO " + studentDTO3.toString()); BeanCopier dtoTOVo = BeanCopier.create(StudentDTO.class, StudentVO.class, false); StudentVO studentVO1 = new StudentVO(); dtoTOVo.copy(studentDTO3, studentVO1, null); System.out.println("cglib: dtoToVo " + studentVO1.toString()); // 一旦使用Converter,BeanCopier隻使用Converter定義的規則去拷貝屬性,所以在convert方法中要考慮所有的屬性 BeanCopier dtoTOVo2 = BeanCopier.create(StudentDTO.class, StudentVO.class, true); StudentVO studentVO2 = new StudentVO(); dtoTOVo2.copy(studentDTO3, studentVO2, new ConvertStudentDtoToVo()); System.out.println("cglib : convert "+studentVO2.toString()); }
四、問題
beanUtils 不會進行 屬性 類型的轉換,如果字段名稱相同,類型不同,不會對該字段進行賦值操作,( 測試方法中使用的 是 org.springframework.beans.BeanUtils )
cglib 在不定義Converter 的情況下也會出現 類型轉換錯誤的異常,可以手動自定義轉換器 convert ,一旦使用Converter,BeanCopier隻使用Converter定義的規則去拷貝屬性,所以在convert方法中要考慮所有的屬性。
springframwork 有實現 cglib 的BeanCopier 不需要再引用 org.easymock 依賴
到此這篇關於深入理解Java對象復制的文章就介紹到這瞭,更多相關Java對象復制內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- MapStruct到底是什麼?
- java開發BeanUtils類解決實體對象間賦值
- springboot 整合fluent mybatis的過程,看這篇夠瞭
- 詳解Java SpringAOP切面類
- Spring使用IOC與DI實現完全註解開發