要拿起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 TASK
和DONE ADDING NEW TASK
行之间发生了什么。如果有人可以向我解释为什么以下行在我的Task
taskList.add(taskService.addTask(newTask.getTitle(), newTask.getDesc(), new Date()));
我还尝试删除了taskList.add()
部分,只对taskService.addTask()
进行了服务呼叫。在这种情况下,没有重复项,并且我的taskList
被更新为一条新记录。但是,我不了解在taskService
类中没有注入的情况下taskList
如何间接修改我的TaskService
。
我错过了什么吗?
答案 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
实际上,这就是真正的服务所要做的。数据将存储在数据库中,但您不会返回对该数据的直接引用。您将始终从数据库中获取当前数据,并将其具体化在全新的列表中。