主题
面试速答(先看这里)
**一句话结论:**Apache的BeanUtils是非常好用的工具,可以帮我们实现对象之间的属性拷贝,但是因为他的设计上太力求做得完美, 在代码中增加了非常多的校验、兼容、日志打印等代码,过度的包装导致性能下降严重。
60秒标准回答:
Apache的BeanUtils是非常好用的工具,可以帮我们实现对象之间的属性拷贝,但是因为他的设计上太力求做得完美, 在代码中增加了非常多的校验、兼容、日志打印等代码,过度的包装导致性能下降严重
市面上有很多类似的工具类,比较常用的有
1、Spring BeanUtils
**答题顺序:**结论 → 原理/机制 → 关键流程 → 场景与取舍 → 易错点
回答主线:
- **要点1:**1、Spring BeanUtils
- **要点2:**3、Apache BeanUtils
- **要点3:**阿里巴巴Java开发手册中提到禁止使用Apache BeanUtils,主要是因为性能问题。
- **要点4:**综上,我们基本可以得出结论,在性能方面,Spring BeanUtils和Cglib BeanCopier表现比较不错,而Apache PropertyUtils、Apache BeanUtils以及Dozer则表现的很不好。
- **要点5:**所以,如果考虑性能情况的话,建议大家不要选择Apache PropertyUtils、Apache BeanUtils以及Dozer等工具类。
**记忆锚点:**Apache → BeanUtils → PropertyUtils → BeanCopier → Spring → Cglib
关键取舍:
- Apache的BeanUtils是非常好用的工具,可以帮我们实现对象之间的属性拷贝,但是因为他的设计上太力求做得完美, 在代码中增加了非常多的校验、兼容、日志打印等代码,过度的包装导致性能下降严重。
- 市面上有很多类似的工具类,比较常用的有 1、Spring BeanUtils 2、Cglib BeanCopier 3、Apache BeanUtils 4、Apache PropertyUtils 阿里巴巴Java开发手册中提到禁止使用Apache BeanUtils,主要是因为性能问题。
- 所以,如果考虑性能情况的话,建议大家不要选择Apache PropertyUtils、Apache BeanUtils以及Dozer等工具类。
易错提醒:
- 所以,如果考虑性能情况的话,建议大家不要选择Apache PropertyUtils、Apache BeanUtils以及Dozer等工具类。
追问准备:
- 围绕「Apache」:底层原理是什么?使用时有哪些边界和常见坑?
- 围绕「BeanUtils」:底层原理是什么?使用时有哪些边界和常见坑?
- 围绕「PropertyUtils」:底层原理是什么?使用时有哪些边界和常见坑?
- 如果线上出现异常,你会如何定位、验证并规避?
典型回答
Apache的BeanUtils是非常好用的工具,可以帮我们实现对象之间的属性拷贝,但是因为他的设计上太力求做得完美, 在代码中增加了非常多的校验、兼容、日志打印等代码,过度的包装导致性能下降严重。
市面上有很多类似的工具类,比较常用的有
1、Spring BeanUtils
2、Cglib BeanCopier
3、Apache BeanUtils
4、Apache PropertyUtils
5、Dozer
阿里巴巴Java开发手册中提到禁止使用Apache BeanUtils,主要是因为性能问题。

实验如下
首先定义一个PersonDO类:
java
public class PersonDO {
private Integer id;
private String name;
private Integer age;
private Date birthday;
//省略setter/getter
}在定义一个PersonDTO:
java
public class PersonDTO {
private String name;
private Integer age;
private Date birthday;
}然后进行测试类的编写,使用Spring BeanUtils进行属性拷贝:
java
private void mappingBySpringBeanUtils(PersonDO personDO, int times) {
StopWatch stopwatch = new StopWatch();
stopwatch.start();
for (int i = 0; i < times; i++) {
PersonDTO personDTO = new PersonDTO();
org.springframework.beans.BeanUtils.copyProperties(personDO, personDTO);
}
stopwatch.stop();
System.out.println("mappingBySpringBeanUtils cost :" + stopwatch.getTotalTimeMillis());
}其中的StopWatch用于记录代码执行时间,方便进行对比。
再使用Cglib BeanCopier进行属性拷贝:
java
private void mappingByCglibBeanCopier(PersonDO personDO, int times) {
StopWatch stopwatch = new StopWatch();
stopwatch.start();
for (int i = 0; i < times; i++) {
PersonDTO personDTO = new PersonDTO();
BeanCopier copier = BeanCopier.create(PersonDO.class, PersonDTO.class, false);
copier.copy(personDO, personDTO, null);
}
stopwatch.stop();
System.out.println("mappingByCglibBeanCopier cost :" + stopwatch.getTotalTimeMillis());
}使用Apache BeanUtils进行属性拷贝:
java
private void mappingByApacheBeanUtils(PersonDO personDO, int times)
throws InvocationTargetException, IllegalAccessException {
StopWatch stopwatch = new StopWatch();
stopwatch.start();
for (int i = 0; i < times; i++) {
PersonDTO personDTO = new PersonDTO();
BeanUtils.copyProperties(personDTO, personDO);
}
stopwatch.stop();
System.out.println("mappingByApacheBeanUtils cost :" + stopwatch.getTotalTimeMillis());
}使用Apache PropertyUtils进行属性拷贝:
java
private void mappingByApachePropertyUtils(PersonDO personDO, int times)
throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
StopWatch stopwatch = new StopWatch();
stopwatch.start();
for (int i = 0; i < times; i++) {
PersonDTO personDTO = new PersonDTO();
PropertyUtils.copyProperties(personDTO, personDO);
}
stopwatch.stop();
System.out.println("mappingByApachePropertyUtils cost :" + stopwatch.getTotalTimeMillis());
}然后执行以下代码:
java
public static void main(String[] args)
throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
PersonDO personDO = new PersonDO();
personDO.setName("Hollis");
personDO.setAge(26);
personDO.setBirthday(new Date());
personDO.setId(1);
MapperTest mapperTest = new MapperTest();
mapperTest.mappingBySpringBeanUtils(personDO, 100);
mapperTest.mappingBySpringBeanUtils(personDO, 1000);
mapperTest.mappingBySpringBeanUtils(personDO, 10000);
mapperTest.mappingBySpringBeanUtils(personDO, 100000);
mapperTest.mappingBySpringBeanUtils(personDO, 1000000);
mapperTest.mappingByCglibBeanCopier(personDO, 100);
mapperTest.mappingByCglibBeanCopier(personDO, 1000);
mapperTest.mappingByCglibBeanCopier(personDO, 10000);
mapperTest.mappingByCglibBeanCopier(personDO, 100000);
mapperTest.mappingByCglibBeanCopier(personDO, 1000000);
mapperTest.mappingByApachePropertyUtils(personDO, 100);
mapperTest.mappingByApachePropertyUtils(personDO, 1000);
mapperTest.mappingByApachePropertyUtils(personDO, 10000);
mapperTest.mappingByApachePropertyUtils(personDO, 100000);
mapperTest.mappingByApachePropertyUtils(personDO, 1000000);
mapperTest.mappingByApacheBeanUtils(personDO, 100);
mapperTest.mappingByApacheBeanUtils(personDO, 1000);
mapperTest.mappingByApacheBeanUtils(personDO, 10000);
mapperTest.mappingByApacheBeanUtils(personDO, 100000);
mapperTest.mappingByApacheBeanUtils(personDO, 1000000);
}得到结果如下:
| 工具类 | 执行1000次耗时 | 执行10000次耗时 | 执行100000次耗时 | 执行1000000次耗时 |
|---|---|---|---|---|
| Spring BeanUtils | 5ms | 10ms | 45ms | 169ms |
| Cglib BeanCopier | 4ms | 18ms | 45ms | 91ms |
| Apache PropertyUtils | 60ms | 265ms | 1444ms | 11492ms |
| Apache BeanUtils | 138ms | 816ms | 4154ms | 36938ms |
| Dozer | 566ms | 2254ms | 11136ms | 102965ms |
画了一张折线图更方便大家进行对比

综上,我们基本可以得出结论,在性能方面,Spring BeanUtils和Cglib BeanCopier表现比较不错,而Apache PropertyUtils、Apache BeanUtils以及Dozer则表现的很不好。
所以,如果考虑性能情况的话,建议大家不要选择Apache PropertyUtils、Apache BeanUtils以及Dozer等工具类。