Лучше один раз сделать, чем десять раз сказать.
задний план:
Недавно начал отвечать за бизнес-модуль с относительно большим объемом данных, требуется узнать все актуальные данные, без подкачки, и организовать древовидную структуру, данные выводятся на фронтенд с dao к сервису, от объекта объекта к объекту Vo. Потом идет копирование объектов. Сначала для конвертации объектов использовался Spring BeanUtils, и проблем не было. Позже, когда я пошел на тестовую площадку, чтобы увеличить объем данных, я обнаружил, что интерфейс становится все медленнее и медленнее Я начал думать, что была проблема с запросом к базе данных и переместил sql.Когда я запускаю базу данных, я обнаружил, что она не медленная, потому что ключевые поля в основном индексируются, поэтому она не будет очень медленной. Я искал шаг за шагом и обнаружил, что это было вызвано тем, что BeanUtils отнимал много времени, а затем есть следующие три метода копирования объектов.
Практика: BeanUtils Apache, BeanUtils Spring, Mapstruct
Может быть много мелких партнеров, которые использовали только Spring BeanUtils, а два других не использовали, но это не имеет значения, давайте проведем простой тест
- Введите зависимости maven, чтобы протестировать проект SpringBoot, созданный непосредственно здесь, используйте тест junit
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- Spring的BeanUtils -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Apache的BeanUtils依赖 -->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.8.3</version>
</dependency>
<!-- mapstruct依赖 -->
<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>
</dependencies>
- структура класса
- 4 простых занятия
/**
* @description user 实体
* @author Wanm
* @date 2020/7/8 21:37
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private String id;
private String name;
private Integer age;
private String address;
private String sex;
}
/**
* @description userVo
* @author Wanm
* @date 2020/7/8 21:37
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserVo {
private String id;
private String name;
private Integer age;
private String address;
private String sex;
}
/**
* @description UserTransfer
* @author Wanm
* @date 2020/7/8 21:37
*/
@Mapper
public interface UserTransfer {
/**
* entity转vo
* @param user
* @return
*/
List<UserVo> entityToVo(List<User> user);
}
/**
* 测试类
*/
@SpringBootTest
class MapstructApplicationTests {
@Test
void contextLoads() {
//这里拿100w数据做数据初始化
List<User> userList = new ArrayList<User>();
for (int i = 0; i < 1000000; i++) {
User user = new User(UUID.randomUUID().toString(),UUID.randomUUID().toString(),i,UUID.randomUUID().toString(),UUID.randomUUID().toString());
userList.add(user);
}
System.out.println(userList.get(0));
System.out.println("开始拷贝---------------------------------------");
testBeanUtils(userList);
testSpringBeanUtils(userList);
testMapStruct(userList);
}
/**
* Apache的BeanUtils
* @param userList
*/
public static void testBeanUtils(List<User> userList){
long start = System.currentTimeMillis();
List<UserVo> userVos = new ArrayList<>();
userList.forEach(item->{
UserVo userVo = new UserVo();
try {
BeanUtils.copyProperties(userVo,item);
userVos.add(userVo);
} catch (Exception e) {
e.printStackTrace();
}
});
long end = System.currentTimeMillis();
System.out.println(userVos.get(0));
System.out.println("集合大小参数验证"+userVos.size()+"Apache的BeanUtils耗时:"+(end-start)+"ms");
}
/**
* Spring的BeanUtils
* @param userList
*/
public static void testSpringBeanUtils(List<User> userList){
long start = System.currentTimeMillis();
List<UserVo> userVos = new ArrayList<>();
userList.forEach(item->{
UserVo userVo = new UserVo();
try {
org.springframework.beans.BeanUtils.copyProperties(item,userVo);
userVos.add(userVo);
} catch (Exception e) {
e.printStackTrace();
}
});
long end = System.currentTimeMillis();
System.out.println(userVos.get(0));
System.out.println("集合大小参数验证"+userVos.size()+"Spring的BeanUtils耗时:"+(end-start)+"ms");
}
/**
* mapStruct拷贝
* @param userList
*/
public void testMapStruct(List<User> userList){
long start = System.currentTimeMillis();
List<UserVo> userVos = Mappers.getMapper(UserTransfer.class).entityToVo(userList);
long end = System.currentTimeMillis();
System.out.println(userVos.get(0));
System.out.println("集合大小参数验证"+userVos.size()+"mapStruct耗时:"+(end-start)+"ms");
}
}
- В реальной разработке поля атрибутов класса vo будут намного меньше, чем у класса сущностей. Просто проведите тест здесь, посмотрите результаты теста ниже
- Вы видели время, которое MapStruct отнимает 41 мс? Это абсолютно лучше. Чем больше объем данных, тем больше вы можете увидеть разницу. На следующем рисунке представлен конкретный анализ времени, требуемого для разных объемов данных:
Apache | Spring | MapStruct | |
---|---|---|---|
1000 | 67ms | 10ms | 2ms |
1w | 174ms | 35ms | 3ms |
10w | 808ms | 69ms | 9ms |
100w | 5620ms | 336ms | 42ms |
Отсюда видно, что чем больше объем данных MapStruct>Spring>Apache, тем более очевидным становится преимущество в производительности. данные большие, рекомендуется использовать метод MapStruct.Повысить производительность интерфейса. Если объем данных невелик, Spring BeanUtils также будет работать, но это зависит от реального бизнес-сценария! ! !
Принцип: MapStruct использует процессор аннотаций для создания класса реализации, класс реализации является собственным новым объектом, а затем назначается метод SetXxx/getXxx для копирования данных, аналогично ломбоку, см. .class класса реализации.
public class UserTransferImpl implements UserTransfer {
public UserTransferImpl() {
}
public List<UserVo> entityToVo(List<User> user) {
if (user == null) {
return null;
} else {
List<UserVo> list = new ArrayList(user.size());
Iterator var3 = user.iterator();
while(var3.hasNext()) {
User user1 = (User)var3.next();
list.add(this.userToUserVo(user1));
}
return list;
}
}
protected UserVo userToUserVo(User user) {
if (user == null) {
return null;
} else {
UserVo userVo = new UserVo();
userVo.setId(user.getId());
userVo.setName(user.getName());
userVo.setAge(user.getAge());
userVo.setAddress(user.getAddress());
userVo.setSex(user.getSex());
return userVo;
}
}
}
Spring и BeanUtils от Apache используют механизм отражения, и внутренняя реализация также различается.В частности, проверьте это.Что касается подробной разницы между ними, оставьте друзьям для самостоятельного изучения~
Резюме: С помощью этого простого теста я освоил методы реализации и различия в производительности трех методов копирования. Тем не менее, рекомендуется, чтобы друзья использовали его в соответствии с реальной сценой. Производительность MapStruct действительно хорошая. Когда необходимо ввести сторонние зависимости. Если объем данных невелик, достаточно BeanUtils, поставляемого с Spring.
Ps:Написано и написано,что ноль часов не успеешь.Если не можешь,торопись и ложись спать.Собачья жизнь важнее. ! !
Я Ян Цин, а не Ян Чжэньцин, мелкий разработчик, борющийся на переднем крае Интернета.