1. 现象
在开发中遇到了一个bug,我在一个对象的 list 属性中add了一个新值,但是接口返回时 list 并没有被改变。
下面我用demo模拟了一下:
定义返回的对象clazz班级,studentsJson 对应数据库中存储的 Json Array 字符串,students是将字符串处理后返回的列表:
public class Clazz {
private String name;
private String studentsJson;
private List<Student> students;
public List<Student> getStudents() {
if (!StringUtils.isEmpty(studentsJson)) {
students = JSON.parseArray(studentsJson, Student.class);
return students;
}
return students;
}
public void setStudents(List<Student> students) {
this.students = students;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getStudentsJson() {
return studentsJson;
}
public void setStudentsJson(String studentsJson) {
this.studentsJson = studentsJson;
}
@Override
public String toString() {
return "Clazz{" +
"name='" + name + '\'' +
", studentsJson='" + studentsJson + '\'' +
", students=" + students +
'}';
}
}
Student对象:
public class Student {
private String name;
// 省略 get/set 方法 ...
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
}
接口模拟:
@RestController
public class TestController {
Logger logger = LoggerFactory.getLogger(TestController.class);
@GetMapping("/test")
public Clazz showClazz() {
// 模拟数据库中查出的对象
Clazz clazz = new Clazz();
clazz.setName("一班");
clazz.setStudentsJson("[{\"name\":\"zhangsan\"},{\"name\":\"lisi\"}]");
logger.info("初始clazz对象: {}", clazz);
List<Student> students = clazz.getStudents();
logger.info("调用getStudents方法获取的students: {}", students);
students.add(new Student("wangwu"));
logger.info("向students中添加一个新的学生后: {}", students);
return clazz;
}
}
请求接口后的输出:
2021-05-20 11:32:28.865 [INFO ] [http-nio-8080-exec-1] [c.jiangxb.test.TestController ] 初始clazz对象: Clazz{name='一班', studentsJson='[{"name":"zhangsan"},{"name":"lisi"}]', students=null}
2021-05-20 11:32:28.916 [INFO ] [http-nio-8080-exec-1] [c.jiangxb.test.TestController ] 调用getStudents方法获取的students: [Student{name='zhangsan'}, Student{name='lisi'}]
2021-05-20 11:32:28.917 [INFO ] [http-nio-8080-exec-1] [c.jiangxb.test.TestController ] 向students中添加一个新的学生后: [Student{name='zhangsan'}, Student{name='lisi'}, Student{name='wangwu'}]
这里students中添加一个应该是三个的,但是用postman调用返回后:
{
"name": "一班",
"studentsJson": "[{\"name\":\"zhangsan\"},{\"name\":\"lisi\"}]",
"students": [
{"name": "zhangsan"},
{"name": "lisi"}
]
}
2. 原因
从controller返回后,spring mvc会对返回值进行处理
ServletInvocableHandlerMethod#invokeAndHandle
this.returnValueHandlers.handleReturnValue(returnValue, this.getReturnValueType(returnValue), mavContainer, webRequest);
在这一步会调用对象的get方法,所以手动加入进students中的对象被覆盖掉了。
3. 建议
建议不要在 get/set 方法中写任何的逻辑,这样虽然可以简化一些代码,但是出现问题时不容易排查,会有很大的隐患,也不利于其他同事修改。
这点在阿里Java开发手册(泰山版)中也有提到: