春季:将一个记录添加到列表中会突然创建一个重复

时间:2018-11-20 07:13:49

标签: java spring spring-mvc spring-boot session

要拿起Spring,我在玩很简单的页面,以大致了解Spring的注释和行为。确切地说,我创建了一个页面来显示Task的列表,并创建了另一个页面来添加新的Task。我每次访问任务列表页面时都使用@SessionAttibutes将Task列表保存在会话中,而不是调用服务来检索Task的完整列表。

这是我的TaskService

@Service
public class TaskService {
  private List<Task> taskCache = new ArrayList<Task>();

  public List<Task> retrieveTasks() {
    System.out.println("GETTING TASK LIST FOR THE FIRST TIME");
    return taskCache;
  }

  public Task addTask(String title, String desc, Date targetDate) {
    Task newTask = new Task(title, desc, targetDate);
    taskCache.add(newTask);

    return newTask;
  }
}

这是我的TaskController

@Controller
@SessionAttributes("taskList")
public class TaskController {
  @Autowired
  private TaskService taskService;

  @GetMapping("/task-view")
  public String showTaskList() {
    return "task/view";
  }

  @GetMapping("/task-add")
  public String showAddTaskForm(@ModelAttribute("task") Task newTask) {
    return "task/add";
  }

  @PostMapping("/task-add")
  public String addTask(ModelMap model, @ModelAttribute("task") Task newTask, @ModelAttribute("taskList") List<Task> taskList) {
    System.out.println("ADDING NEW TASK: " + taskList.size());
    taskList.add(taskService.addTask(newTask.getTitle(), newTask.getDesc(), new Date()));
    System.out.println("DONE ADDING NEW TASK: " + taskList.size());
    return "redirect:/task-view";
  }

  @ModelAttribute("taskList")
  public List<Task> taskList() {
    System.out.println("CALLING TASK SERVICE TO GET LIST OF TASKS");
    return taskService.retrieveTasks();
  }
}

这是我的view.jsp

<html>
  <head>
    <title>Task Page</title>
  </head>
  <body>
    This is your task list:
    ${taskList}

    <div>
      <a href="task-add">Add a task</a>
    </div>
  </body>
</html>

这是我的add.jsp

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<html>
  <head>
    <title>Task Page</title>
  </head>
  <body>
    <form:form method="POST" action="/task-add" modelAttribute="task">
      <div>
        <form:label path="title">Title</form:label>
        <form:input path="title" />
      </div>

      <div>
        <form:label path="desc">Description</form:label>
        <form:input path="desc" />
      </div>

      <div>
        <input type="submit" value="Add">
      </div>
    </form:form>
  </body>
</html>

就session-scoped属性而言,它按预期工作。我第一次访问TaskService.retrieveTasks()时仅调用view.jsp函数。随后,从会话中提取相同的任务列表。

问题是,当我尝试在Task页上添加两个新的add.jsp时,我在控制台中看到了以下几行。

CALLING TASK SERVICE TO GET LIST OF TASKS
GETTING TASK LIST FOR THE FIRST TIME
ADDING NEW TASK: 0
DONE ADDING NEW TASK: 2
ADDING NEW TASK: 2
DONE ADDING NEW TASK: 4

当我第一次访问view.jsp时,打印了第一行和第二行,这是正确的。但是,我不知道ADDING NEW TASKDONE ADDING NEW TASK行之间发生了什么。如果有人可以向我解释为什么以下行在我的Task

列表中添加重复项,我将不胜感激
taskList.add(taskService.addTask(newTask.getTitle(), newTask.getDesc(), new Date()));

我还尝试删除了taskList.add()部分,只对taskService.addTask()进行了服务呼叫。在这种情况下,没有重复项,并且我的taskList被更新为一条新记录。但是,我不了解在taskService类中没有注入的情况下taskList如何间接修改我的TaskService

我错过了什么吗?

2 个答案:

答案 0 :(得分:2)

  @ModelAttribute("taskList")
  public List<Task> taskList() {
    System.out.println("CALLING TASK SERVICE TO GET LIST OF TASKS");
    return taskService.retrieveTasks();
  }

我认为这就是您所缺少的。 taskList()方法返回taskCache列表,即taskService修改的列表。因此,taksList和taskCache是​​同一对象。

答案 1 :(得分:1)

问题出在方法readBinaryFiles中:

retrieveTasks

此方法返回对public List<Task> retrieveTasks() { System.out.println("GETTING TASK LIST FOR THE FIRST TIME"); return taskCache; } 对象的引用。有权访问此参考的每个人都可以修改此可变列表。例如。在PHP中,如果返回一个数组,则默认情况下会收到该数组的副本。不是Java。

您应该像这样修改方法:

taskCache

实际上,这就是真正的服务所要做的。数据将存储在数据库中,但您不会返回对该数据的直接引用。您将始终从数据库中获取当前数据,并将其具体化在全新的列表中。