Il existe plusieurs bibliothèques couramment utilisées lors de la copie de beans à l'aide de Java.
Parmi ceux-ci, celui que je recommande le plus est «mapstruct». La raison en est que «mapstruct» est ** le plus rapide **. Les sources suivantes indiquent le temps nécessaire à chaque copie de bean en utilisant les cinq bibliothèques ci-dessus.
@Slf4j
public class CopyDemoTest {
public UserMainBO bo;
public static int count = 1000000;
@Before
public void init(){
bo = new UserMainBO();
bo.setId(1L);
}
@Test
public void mapstruct() {
UserMainVOMapping INSTANCE = Mappers.getMapper( UserMainVOMapping.class );
log.info("star------------");
for (int i = 1; i <=count; i++) {
UserMainVO vo = INSTANCE.toVO(bo);
}
log.info("end------------");
}
@Test
public void beanCopier() {
log.info("star------------");
BeanCopier copier = BeanCopier.create(UserMainBO.class, UserMainVO.class, false);
for (int i = 1; i <=count; i++) {
UserMainVO vo = new UserMainVO();
copier.copy(bo, vo, null);
}
log.info("end------------");
}
@Test
public void springBeanUtils(){
log.info("star------------");
for (int i = 1; i <=count; i++) {
UserMainVO vo = new UserMainVO();
BeanUtils.copyProperties(bo, vo);
}
log.info("end------------");
}
@Test
public void apacheBeanUtils() throws InvocationTargetException, IllegalAccessException {
for (int i = 1; i <=count; i++) {
UserMainVO vo = new UserMainVO();
org.apache.commons.beanutils.BeanUtils.copyProperties(bo, vo);
}
log.info("end------------");
}
@Test
public void apachePropertyUtils() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
log.info("star------------");
for (int i = 1; i <=count; i++) {
UserMainVO vo = new UserMainVO();
PropertyUtils.copyProperties(bo, vo);
}
log.info("end------------");
}
}
résultat de l'inspection:
1,000 fois | 10,000 fois | 100,000 fois | 1,000,000 fois | |
---|---|---|---|---|
apache.BeanUtils | 550ms | 1085ms | 4287ms | 32088ms |
apache.PropertyUtils | 232ms | 330ms | 2080ms | 20681ms |
cglib.BeanCopier | 73ms | 106ms | 102ms | 99ms |
mapstruct | 91ms | 5ms | 7ms | 12ms |
spring.BeanUtils | 5ms | 188ms | 336ms | 844ms |
À partir des résultats de vérification ci-dessus, il peut être confirmé que mapstruct a de bien meilleures performances que les autres bibliothèques.
Pour utiliser MapStruct, vous devez importer la bibliothèque MapStruct dans votre projet. Voici un exemple d'utilisation de maven.
pom.xml
<properties>
<org.mapstruct.version>1.3.1.Final</org.mapstruct.version>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${org.projectlombok.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source> <!-- depending on your project -->
<target>1.8</target> <!-- depending on your project -->
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
Lorsque vous utilisez MapStruct avec la version 1.2.0.Final
ou antérieure et lombok ensemble, les paramètres suivants sont requis:
pom.xml
<properties>
<org.mapstruct.version>1.1.0.Final</org.mapstruct.version>
<org.projectlombok.version>1.18.12</org.projectlombok.version>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${org.projectlombok.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source> <!-- depending on your project -->
<target>1.8</target> <!-- depending on your project -->
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${org.projectlombok.version}</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
Puisque l'intérieur de swagger-ui utilise une ancienne version de MapSruct, il est nécessaire d'exclure la dépendance sur MapSruct de la bibliothèque swagger-ui lors de son utilisation avec swagger-ui.
pom.xml
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${springfox-swagger.version}</version>
<exclusions>
<exclusion>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${springfox-swagger.version}</version>
<exclusions>
<exclusion>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
</exclusion>
</exclusions>
</dependency>
Il existe deux classes de haricots ci-dessous.
Student.java
import lombok.Data;
import lombok.ToString;
@Data
@ToString
public class Student {
private String name;
private String address;
private String phone;
}
Person.java
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Person {
private String name;
private String address;
private String telephone;
}
A partir de maintenant, bean copie de l'objet Person vers l'objet Student. Avant de copier le bean, il est nécessaire de préparer la classe Mapper à l'avance.
StudentMapper.java
import model.Person;
import model.Student;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface StudentMapper {
StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);
Student personToStudent(Person person); //Le nom de la méthode est arbitraire
}
Copions le haricot.
main_method
Person person = new Person("lisa", "Tokyo", "12345");
Student student = StudentMapper.INSTANCE.personToStudent(person);
System.out.println(student);
//production
// Student(name=Lisa, address=Tokyo, phone=null)
Par défaut, tout champ dont le nom ne correspond pas est automatiquement ** ignoré ** et ne peut pas être copié de «téléphone» vers «téléphone».
Si les noms des champs source et cible ne correspondent pas, vous devez spécifier @ Mapping
lorsque vous essayez de copier.
StudentMapper.java
import model.Person;
import model.Student;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
@Mapper
public interface StudentMapper {
StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);
@Mapping(source = "telephone", target = "phone")
Student personToStudent(Person person);
}
Résultat de l'exécution:
Student(name=Lisa, address=Tokyo, phone=12345)
Si vous ne souhaitez copier aucun champ, vous pouvez les exclure de la copie via l'attribut @ Mapping
ʻignore`.
StudentMapper.java
import model.Person;
import model.Student;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
@Mapper
public interface StudentMapper {
StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);
@Mapping(source = "telephone", target = "phone")
@Mapping(target = "address", ignore = true)
Student personToStudent(Person person);
}
Résultat de l'exécution:
Student(name=Lisa, address=null, phone=12345)
MapStruct peut copier des champs de plusieurs beans vers un seul bean.
LoginInfo.java
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginInfo {
private String id;
private String password;
}
UserProfile.java
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserProfile {
private String email;
private String address;
}
UserInfo.java
@ToString
@Data
public class UserInfo {
private String id;
private String password;
private String email;
private String address;
}
Préparez Mapper.
UserInfoMapper.java
import model.LoginInfo;
import model.UserInfo;
import model.UserProfile;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
@Mapper
public interface UserInfoMapper {
UserInfoMapper INSTANCE = Mappers.getMapper(UserInfoMapper.class);
@Mapping(target = "id", source = "loginInfo.id")
@Mapping(target = "password", source = "loginInfo.password")
@Mapping(target = "email", source = "userProfile.email")
@Mapping(target = "address", source = "userProfile.address")
UserInfo fromLoginInfoAndUserProfile(LoginInfo loginInfo, UserProfile userProfile);
}
faire une copie.
main_method
LoginInfo loginInfo = new LoginInfo("12345", "54321");
UserProfile userProfile = new UserProfile("[email protected]", "Tokyo");
UserInfo userInfo = UserInfoMapper.INSTANCE.fromLoginInfoAndUserProfile(loginInfo, userProfile);
System.out.println(userInfo);
//production
// UserInfo(id=12345, password=54321, [email protected], address=Tokyo)
MapStruct peut non seulement copier et créer des beans, mais également mettre à jour les beans créés.
ʻAjoutez la source suivante à UserInfoMapper.java`:
UserInfoMapper.java
@Mapping(target = "email", source = "email")
@Mapping(target = "address", source = "address")
void updateUserProfile(UserProfile userProfile, @MappingTarget UserInfo userInfo);
Mettons à jour:
main_method
LoginInfo loginInfo = new LoginInfo("12345", "54321");
UserProfile userProfile = new UserProfile("[email protected]", "Tokyo");
UserInfo userInfo = UserInfoMapper.INSTANCE.fromLoginInfoAndUserProfile(loginInfo, userProfile);
System.out.println(userInfo);
//production
// UserInfo(id=12345, password=54321, [email protected], address=Tokyo)
userProfile = new UserProfile("[email protected]", "Fukuoka");
UserInfoMapper.INSTANCE.updateUserProfile(userProfile, userInfo);
System.out.println(userInfo);
//production
// UserInfo(id=12345, password=54321, [email protected], address=Fukuoka)
Dans MapStruct, lors de la conversion de Date / Date locale en String, lors de la conversion de int en String, vous pouvez spécifier le format et convertir.
Person.java
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.time.LocalDate;
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private String name;
private LocalDate birthday;
private int salary;
}
Employee.java
import lombok.Data;
import lombok.ToString;
@Data
@ToString
public class Employee {
private String name;
private String birthday;
private String salary;
}
PersonMapper.java
import model.Employee;
import model.Person;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
@Mapper
public interface PersonMapper {
PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class);
@Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy/MM/dd")
@Mapping(source = "salary", target = "salary", numberFormat = "#,###")
Employee personToEmployee(Person person);
}
main_method
Person person = new Person("lisa", LocalDate.of(1990, 1, 20), 1234567);
Employee employee = PersonMapper.INSTANCE.personToEmployee(person);
System.out.println(employee);
//production
// Employee(name=lisa, birthday=1990/01/20, salary=1,234,567)
3.7 expression
Lors de la copie d'un bean, si la complexité doit être convertie, elle peut être facilement réalisée avec ʻexpression`.
Par exemple, mettez en majuscule le champ name
lors de la copie d'un objet Person vers un objet Employee.
PersonMapper.java
import model.Employee;
import model.Person;
import org.apache.commons.lang3.StringUtils;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
@Mapper(imports = {StringUtils.class}) //Importer des StringUtils
public interface PersonMapper {
PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class);
@Mapping(target = "name", expression = "java(StringUtils.upperCase(person.getName()))")
Employee personToEmployee(Person person);
}
À partir de Java8, l'interface peut prendre en charge la méthode par défaut. MapStruct peut également utiliser la méthode par défaut pour créer une logique de copie complexe.
PersonMapper.java
import model.Employee;
import model.Person;
import org.apache.commons.lang3.StringUtils;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import java.time.format.DateTimeFormatter;
@Mapper(imports = {StringUtils.class})
public interface PersonMapper {
PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class);
default Employee personToEmployee(Person person, String dateFormat) {
Employee employee = new Employee();
//Mettre en majuscule le nom
employee.setName(StringUtils.upperCase(person.getName()));
//Convertir l'anniversaire au format spécifié
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(dateFormat);
employee.setBirthday(person.getBirthday().format(dateTimeFormatter));
return employee;
}
}
Exécutez la méthode par défaut.
main_method
Person person = new Person("lisa", LocalDate.of(1990, 1, 20), 1234567);
Employee employee = PersonMapper.INSTANCE.personToEmployee(person, "aaaa année MM mois jj jour");
System.out.println(employee);
//production
// Employee(name=LISA, birthday=20 janvier 1990, salary=null)