日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring –添加Spring MVC –第2部分

發(fā)布時(shí)間:2023/12/3 javascript 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring –添加Spring MVC –第2部分 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
在上一部分中,我們?yōu)榻?jīng)理和員工實(shí)現(xiàn)了控制器。 既然我們知道了自己的出路,我們將做很少(但僅做很少)更復(fù)雜的事情-任務(wù)和時(shí)間表的控制器。

因此,讓我們從org.timesheet.web開始。 TaskController 。 首先創(chuàng)建一個(gè)類,這次我們將訪問更豐富的域,因此我們需要為任務(wù),員工和經(jīng)理自動(dòng)連接三個(gè)DAOS。

@Controller @RequestMapping('/tasks') public class TaskController {private TaskDao taskDao;private EmployeeDao employeeDao;private ManagerDao managerDao;@Autowiredpublic void setTaskDao(TaskDao taskDao) {this.taskDao = taskDao;}@Autowiredpublic void setEmployeeDao(EmployeeDao employeeDao) {this.employeeDao = employeeDao;}@Autowiredpublic void setManagerDao(ManagerDao managerDao) {this.managerDao = managerDao;}public EmployeeDao getEmployeeDao() {return employeeDao;}public TaskDao getTaskDao() {return taskDao;}public ManagerDao getManagerDao() {return managerDao;} }

讓我們處理/ tasks上的GET請(qǐng)求:

/*** Retrieves tasks, puts them in the model and returns corresponding view* @param model Model to put tasks to* @return tasks/list*/@RequestMapping(method = RequestMethod.GET)public String showTasks(Model model) {model.addAttribute('tasks', taskDao.list());return 'tasks/list';}

我們將把JSP放在任務(wù)子文件夾中。 首先是用于顯示所有任務(wù)的list.jsp。 它不僅遍歷所有任務(wù),而且在每個(gè)任務(wù)上遍歷員工:

<%@ page contentType='text/html;charset=UTF-8' language='java' %> <%@ taglib prefix='fmt' uri='http://java.sun.com/jsp/jstl/fmt' %> <%@ taglib prefix='spring' uri='http://www.springframework.org/tags' %> <%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> <%@ taglib prefix='sf' uri='http://www.springframework.org/tags/form'%><!-- resolve variables --> <%--@elvariable id='tasks' type='java.util.List<org.timesheet.domain.Task>'--%><html> <head><title>Tasks</title><link rel='stylesheet' href='/timesheet-app/resources/style.css' type='text/css'> </head> <body><h1>List of tasks</h1><a href='tasks?new'>Add new task</a><table cellspacing='5' class='main-table wide'><tr><th style='width: 35%;'>Description</th><th>Manager</th><th>Employees</th><th>Completed</th><th style='width: 20%;'>Details</th><th>Delete</th></tr><c:forEach items='${tasks}' var='task'><tr><td>${task.description}</td><td><a href='managers/${task.manager.id}'>${task.manager.name}</a></td><td><c:forEach items='${task.assignedEmployees}' var='emp'><a href='employees/${emp.id}'>${emp.name}</a></c:forEach></td><td><div class='delete'><c:choose><c:when test='${task.completed}'>Done</c:when><c:when test='${!task.completed}'>In progress</c:when></c:choose></div></td><td><a href='tasks/${task.id}'>Go to page</a></td><td><sf:form action='tasks/${task.id}' method='delete' cssClass='delete'><input type='submit' value='' class='delete-button' /></sf:form></td></tr></c:forEach></table><br /><a href='welcome'>Go back</a> </body> </html>

照常刪除任務(wù):

/*** Deletes task with specified ID* @param id Task's ID* @return redirects to tasks if everything was ok* @throws TaskDeleteException When task cannot be deleted*/@RequestMapping(value = '/{id}', method = RequestMethod.DELETE)public String deleteTask(@PathVariable('id') long id) throws TaskDeleteException {Task toDelete = taskDao.find(id);boolean wasDeleted = taskDao.removeTask(toDelete);if (!wasDeleted) {throw new TaskDeleteException(toDelete);}// everything OK, see remaining tasksreturn 'redirect:/tasks';}

TaskDeleteException:

package org.timesheet.web.exceptions;import org.timesheet.domain.Task;/*** When task cannot be deleted.*/ public class TaskDeleteException extends Exception {private Task task;public TaskDeleteException(Task task) {this.task = task;}public Task getTask() {return task;} }

處理此異常的方法:

/*** Handles TaskDeleteException* @param e Thrown exception with task that couldn't be deleted* @return binds task to model and returns tasks/delete-error*/@ExceptionHandler(TaskDeleteException.class)public ModelAndView handleDeleteException(TaskDeleteException e) {ModelMap model = new ModelMap();model.put('task', e.getTask());return new ModelAndView('tasks/delete-error', model);}

JSP頁面jsp / tasks / delete-error.jsp用于顯示刪除錯(cuò)誤:

<%--@elvariable id='task' type='org.timesheet.domain.Task'--%><html> <head><title>Cannot delete task</title> </head> <body>Oops! Resource <a href='${task.id}'>${task.description}</a> can not be deleted.<p>Make sure there are no timesheets assigned on task.</p><br /><br /><br /><a href='../welcome'>Back to main page.</a> </body> </html>

顯示任務(wù)的詳細(xì)信息將通過URI / tasks / {id}訪問。 我們將同時(shí)在模型中添加任務(wù)和可以添加到任務(wù)中的未分配員工。 它將像這樣處理:

/*** Returns task with specified ID* @param id Tasks's ID* @param model Model to put task to* @return tasks/view*/@RequestMapping(value = '/{id}', method = RequestMethod.GET)public String getTask(@PathVariable('id') long id, Model model) {Task task = taskDao.find(id);model.addAttribute('task', task);// add all remaining employeesList<Employee> employees = employeeDao.list();Set<Employee> unassignedEmployees = new HashSet<Employee>();for (Employee employee : employees) {if (!task.getAssignedEmployees().contains(employee)) {unassignedEmployees.add(employee);}}model.addAttribute('unassigned', unassignedEmployees);return 'tasks/view';}

現(xiàn)在,事情有些復(fù)雜了。 我們想顯示任務(wù)的用戶詳細(xì)信息頁面。 在此任務(wù)上,我們要添加/刪除分配給它的員工。
首先,讓我們考慮一下URL。 任務(wù)已分配了員工,因此用于訪問任務(wù)中員工的URL將如下所示:
/ tasks / {id} / employees / {employeeId} 要?jiǎng)h除員工,我們只需使用DELETE方法訪問此資源,因此讓我們向控制器添加方法:

/*** Removes assigned employee from task* @param taskId Task's ID* @param employeeId Assigned employee's ID*/@RequestMapping(value = '/{id}/employees/{employeeId}', method = RequestMethod.DELETE)@ResponseStatus(HttpStatus.NO_CONTENT)public void removeEmployee(@PathVariable('id') long taskId,@PathVariable('employeeId') long employeeId) {Employee employee = employeeDao.find(employeeId);Task task = taskDao.find(taskId);task.removeEmployee(employee);taskDao.update(task);}

在視圖頁面上(我們稍后會(huì)看到),我們將簡(jiǎn)單地使用jQuery更改DOM模型,并從列表中刪除分配的員工。
讓我們假裝什么都不會(huì)出錯(cuò)(我們有NO_CONTENT響應(yīng)),因此員工將總是成功地從數(shù)據(jù)庫中刪除。 因此,我們可以簡(jiǎn)單地更改DOM模型。

對(duì)于添加員工,我們將有未分配員工的選擇列表(或組合框)。 刪除員工后,我們會(huì)將其添加到可用員工的選擇中(他再次可用)。 添加員工后,我們將使用DAO更改Task并將其重定向回同一任務(wù)(所有內(nèi)容都會(huì)更新)。 這是將員工分配給任務(wù)的代碼:

/*** Assigns employee to tak* @param taskId Task's ID* @param employeeId Employee's ID (to assign)* @return redirects back to altered task: tasks/taskId*/@RequestMapping(value = '/{id}/employees/{employeeId}', method = RequestMethod.PUT)public String addEmployee(@PathVariable('id') long taskId,@PathVariable('employeeId') long employeeId) {Employee employee = employeeDao.find(employeeId);Task task = taskDao.find(taskId);task.addEmployee(employee);taskDao.update(task);return 'redirect:/tasks/' + taskId;}

最后,使用task / view.jsp了解Task的詳細(xì)信息。 正如我提到的那樣,有很多DOM更改,因此此代碼似乎比平時(shí)更難。

<%@ page contentType='text/html;charset=UTF-8' language='java' %> <%@ taglib prefix='sf' uri='http://www.springframework.org/tags/form'%> <%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%><%--@elvariable id='task' type='org.timesheet.domain.Task'--%> <%--@elvariable id='unassigned' type='java.util.List<org.timesheet.domain.Employee>'--%><html> <head><title>Task page</title><link rel='stylesheet' href='/timesheet-app/resources/style.css' type='text/css'> </head> <body><h2>Task info</h2><div id='list'><ul><li><label for='description'>Description:</label><input name='description' id='description' value='${task.description}'disabled='${task.completed ? 'disabled' : ''}' /></li><li><label for='manager'>Manager:</label><input name='manager' id='manager' value='${task.manager.name}'disabled='true' /></li><li><label for='employees'>Employees:</label><table id='employees' class='task-table'><c:forEach items='${task.assignedEmployees}' var='emp'><tr><sf:form action='${task.id}/employees/${emp.id}' method='delete'><td><a href='../employees/${emp.id}' id='href-${emp.id}'>${emp.name}</a></td><td><input type='submit' value='Remove' id='remove-${emp.id}' /><script src='/timesheet-app/resources/jquery-1.7.1.js'></script><script type='text/javascript'>$('#remove-${emp.id}').on('click', function() {$('#remove-${emp.id}').addClass('hidden');$('#href-${emp.id}').remove();// add to list of unassignedvar opt = document.createElement('option');opt.setAttribute('value', '${emp.id}');opt.textContent = '${emp.name}';$('#selected-emp').append(opt);});</script></td></sf:form></tr></c:forEach></table></li><li><label for='unassigned'>Unassgined:</label><table id='unassigned' class='task-table'><tr><sf:form method='put' id='add-form'><td><select id='selected-emp'><c:forEach items='${unassigned}' var='uemp'><option value='${uemp.id}'>${uemp.name}</option></c:forEach></select></td><td><input type='submit' value='Add' id='add-employee' /><script src='/timesheet-app/resources/jquery-1.7.1.js'></script><script type='text/javascript'>$('#add-employee').on('click', function() {$('#selected-emp').selected().remove();});</script></td></sf:form></tr></table></li></ul></div><br /><br /><a href='../tasks'>Go Back</a><script src='/timesheet-app/resources/jquery-1.7.1.js'></script><script type='text/javascript'>(function() {// prepare default form actionsetAddAction();// handler for changing action$('#selected-emp').on('change', function() {setAddAction();});function setAddAction() {var id = $('#selected-emp').val();$('#add-form').attr('action', '${task.id}/employees/' + id);}})();</script> </body> </html>

從代碼中可以看到,我們?cè)俅蝺H使用HTML + JavaScript。 唯一特定于JSP的是將數(shù)據(jù)從模型帶到頁面。

OK,現(xiàn)在我們必須能夠創(chuàng)建新的Task。 讓我們準(zhǔn)備用于提供表單的控制器,以添加將從/ tasks?new訪問的任務(wù):

/*** Creates form for new task.* @param model Model to bind to HTML form* @return tasks/new*/@RequestMapping(params = 'new', method = RequestMethod.GET)public String createTaskForm(Model model) {model.addAttribute('task', new Task());// list of managers to choose fromList<Manager> managers = managerDao.list();model.addAttribute('managers', managers);return 'tasks/new';}

任務(wù)包括名稱,經(jīng)理和分配的員工。 在本教程的范圍內(nèi),我決定不實(shí)施最后一個(gè)。 我們只會(huì)產(chǎn)生一些員工。 如果您希望能夠從某種選擇列表中挑選員工并將他們分配給任務(wù),那么請(qǐng)注意,這應(yīng)該異步進(jìn)行。 為此,您可以將特殊方法映射到控制器,并使用$ .post與jQuery進(jìn)行AJAX發(fā)布。 我認(rèn)為對(duì)于本教程來說,這太少了,但是如果您對(duì)如何在Spring中使用AJAX感興趣,請(qǐng)查看有關(guān)Spring 3中的ajax簡(jiǎn)化的博客文章 。
在創(chuàng)建員工和經(jīng)理時(shí),我們僅將原始類型用于屬性。 現(xiàn)在,我們想為任務(wù)分配實(shí)際的Manager實(shí)例。 因此,我們必須告訴Spring如何將選擇列表(經(jīng)理的ID)中的值轉(zhuǎn)換為實(shí)際實(shí)例。 為此,我們將使用自定義的PropertyEditorSupport工具。 添加新的org.timesheet.web.editors包,創(chuàng)建新類ManagerEditor與下面的代碼:

public class ManagerEditor extends PropertyEditorSupport {private ManagerDao managerDao;public ManagerEditor(ManagerDao managerDao) {this.managerDao = managerDao;}@Overridepublic void setAsText(String text) throws IllegalArgumentException {long id = Long.parseLong(text);Manager manager = managerDao.find(id);setValue(manager);} }

ManagerEditor將在其構(gòu)造函數(shù)中傳遞DAO。 它將通過ID查找實(shí)際的管理員,并調(diào)用父級(jí)的setValue。
Spring現(xiàn)在應(yīng)該知道有這樣一個(gè)編輯器,因此我們必須在控制器中注冊(cè)它。 我們只需要將WebDataBinder作為參數(shù)的方法,并需要使用@InitBinder注釋對(duì)其進(jìn)行注釋,如下所示:

@InitBinderprotected void initBinder(WebDataBinder binder) {binder.registerCustomEditor(Manager.class, new ManagerEditor(managerDao));}

就是這樣,Spring現(xiàn)在知道如何直接從表單將經(jīng)理分配給我們的任務(wù)。

最后保存任務(wù)的代碼。 如前所述,在保存之前,我們將招募一些員工來執(zhí)行任務(wù):

/*** Saves new task to the database* @param task Task to save* @return redirects to tasks*/@RequestMapping(method = RequestMethod.POST)public String addTask(Task task) {// generate employeesList<Employee> employees = reduce(employeeDao.list());task.setAssignedEmployees(employees);taskDao.add(task);return 'redirect:/tasks';}

reduce方法,這是減少內(nèi)存中員工的簡(jiǎn)單輔助方法。 這并不是十分有效,我們可以使用更復(fù)雜的查詢來做到這一點(diǎn),但現(xiàn)在就可以了。 如果需要,也可以隨意滾動(dòng)自己的歸約邏輯:

/*** Reduces list of employees to some smaller amount.* Simulates user interaction.* @param employees Employees to reduced* @return New list of some employees from original employees list*/private List<Employee> reduce(List<Employee> employees) {List<Employee> reduced = new ArrayList<Employee>();Random random = new Random();int amount = random.nextInt(employees.size()) + 1;// max. five employeesamount = amount > 5 ? 5 : amount;for (int i = 0; i < amount; i++) {int randomIdx = random.nextInt(employees.size());Employee employee = employees.get(randomIdx);reduced.add(employee);employees.remove(employee);}return reduced;}

現(xiàn)在讓我們看一下task / new.jsp頁面:

<%@ page contentType='text/html;charset=UTF-8' language='java' %> <%@ taglib prefix='sf' uri='http://www.springframework.org/tags/form'%> <%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%><%--@elvariable id='task' type='org.timesheet.domain.Task'--%> <%--@elvariable id='managers' type='java.util.List<org.timesheet.domain.Manager'--%><html> <head><title>Add new task</title><link rel='stylesheet' href='/timesheet-app/resources/style.css' type='text/css'> </head> <body><h2>Add new Task</h2><div id='list'><sf:form method='post' action='tasks' commandName='task'><ul><li><label for='description'>Description:</label><input name='description' id='description' value='${task.description}' /></li><li><label for='manager-select'>Manager:</label><sf:select path='manager' id='manager-select'><sf:options items='${managers}' itemLabel='name' itemValue='id' /></sf:select></li><li>Employees will be generated ...</li><li><input type='submit' value='Save'></li></ul></sf:form></div><br /><br /><a href='tasks'>Go Back</a></body> </html>

當(dāng)然要測(cè)試控制器:

package org.timesheet.web;import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.ui.ExtendedModelMap; import org.springframework.ui.Model; import org.springframework.web.servlet.ModelAndView; import org.timesheet.DomainAwareBase; import org.timesheet.domain.Employee; import org.timesheet.domain.Manager; import org.timesheet.domain.Task; import org.timesheet.service.dao.EmployeeDao; import org.timesheet.service.dao.ManagerDao; import org.timesheet.service.dao.TaskDao; import org.timesheet.web.exceptions.TaskDeleteException;import java.util.Collection; import java.util.List;import static org.junit.Assert.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when;@ContextConfiguration(locations = {'/persistence-beans.xml', '/controllers.xml'}) public class TaskControllerTest extends DomainAwareBase {private Model model; // used for controller@Autowiredprivate TaskDao taskDao;@Autowiredprivate ManagerDao managerDao;@Autowiredprivate EmployeeDao employeeDao;@Autowiredprivate TaskController controller;@Beforepublic void setUp() {model = new ExtendedModelMap(); }@Afterpublic void cleanUp() {List<Task> tasks = taskDao.list();for (Task task : tasks) {taskDao.remove(task);}}@Testpublic void testShowTasks() {// prepare some dataTask task = sampleTask();// use controllerString view = controller.showTasks(model);assertEquals('tasks/list', view);List<Task> listFromDao = taskDao.list();Collection<?> listFromModel = (Collection<?>) model.asMap ().get('tasks');assertTrue(listFromModel.contains(task));assertTrue(listFromDao.containsAll(listFromModel));}@Testpublic void testDeleteTaskOk() throws TaskDeleteException {Task task = sampleTask();long id = task.getId();// delete & assertString view = controller.deleteTask(id);assertEquals('redirect:/tasks', view);assertNull(taskDao.find(id));}@Test(expected = TaskDeleteException.class)public void testDeleteTaskThrowsException() throws TaskDeleteException {Task task = sampleTask();long id = task.getId();// mock DAO for this callTaskDao mockedDao = mock(TaskDao.class);when(mockedDao.removeTask(task)).thenReturn(false);TaskDao originalDao = controller.getTaskDao();try {// delete & expect exceptioncontroller.setTaskDao(mockedDao);controller.deleteTask(id);} finally {controller.setTaskDao(originalDao);}}@Testpublic void testHandleDeleteException() {Task task = sampleTask();TaskDeleteException e = new TaskDeleteException(task);ModelAndView modelAndView = controller.handleDeleteException(e);assertEquals('tasks/delete-error', modelAndView.getViewName());assertTrue(modelAndView.getModelMap().containsValue(task));}@Testpublic void testGetTask() {Task task = sampleTask();long id = task.getId();// get & assertString view = controller.getTask(id, model);assertEquals('tasks/view', view);assertEquals(task, model.asMap().get('task'));}@Testpublic void testRemoveEmployee() {Task task = sampleTask();long id = task.getAssignedEmployees().get(0).getId();controller.removeEmployee(task.getId(), id);// task was updated inside controller in other transaction -> refreshtask = taskDao.find(task.getId());// get employee & assertEmployee employee = employeeDao.find(id);assertFalse(task.getAssignedEmployees().contains(employee));}@Testpublic void testAddEmployee() {Task task = sampleTask();Employee cassidy = new Employee('Butch Cassidy', 'Cowboys');employeeDao.add(cassidy);controller.addEmployee(task.getId(), cassidy.getId());// task was updated inside controller in other transaction -> refreshtask = taskDao.find(task.getId());// get employee & assertEmployee employee = employeeDao.find(cassidy.getId());assertTrue(task.getAssignedEmployees().contains(employee));}@Testpublic void testAddTask() {Task task = sampleTask();// save via controllerString view = controller.addTask(task);assertEquals('redirect:/tasks', view);// task is in DBassertEquals(task, taskDao.find(task.getId()));}private Task sampleTask() {Manager manager = new Manager('Jesse James');managerDao.add(manager);Employee terrence = new Employee('Terrence', 'Cowboys');Employee kid = new Employee('Sundance Kid', 'Cowboys');employeeDao.add(terrence);employeeDao.add(kid);Task task = new Task('Wild West', manager, terrence, kid);taskDao.add(task);return task;} }

任務(wù)就是這樣。 現(xiàn)在讓我們?yōu)闀r(shí)間表創(chuàng)建控制器。 為我們需要的控制器和自動(dòng)接線的DAO添加基本樣板:

@Controller @RequestMapping('/timesheets') public class TimesheetController {private TimesheetDao timesheetDao;private TaskDao taskDao;private EmployeeDao employeeDao;@Autowiredpublic void setTimesheetDao(TimesheetDao timesheetDao) {this.timesheetDao = timesheetDao;}@Autowiredpublic void setTaskDao(TaskDao taskDao) {this.taskDao = taskDao;}@Autowiredpublic void setEmployeeDao(EmployeeDao employeeDao) {this.employeeDao = employeeDao;}public TimesheetDao getTimesheetDao() {return timesheetDao;}public TaskDao getTaskDao() {return taskDao;}public EmployeeDao getEmployeeDao() {return employeeDao;} }

在時(shí)間表上處理GET請(qǐng)求的方法:

/*** Retrieves timesheets, puts them in the model and returns corresponding view* @param model Model to put timesheets to* @return timesheets/list*/@RequestMapping(method = RequestMethod.GET)public String showTimesheets(Model model) {List<Timesheet> timesheets = timesheetDao.list();model.addAttribute('timesheets', timesheets);return 'timesheets/list';}

JSP將放置在時(shí)間表子文件夾中。 添加list.jsp頁面,該頁面基本上將遍歷Timesheet的屬性并滾動(dòng)刪除表單:

<%@ page contentType='text/html;charset=UTF-8' language='java' %> <%@ taglib prefix='fmt' uri='http://java.sun.com/jsp/jstl/fmt' %> <%@ taglib prefix='spring' uri='http://www.springframework.org/tags' %> <%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> <%@ taglib prefix='sf' uri='http://www.springframework.org/tags/form'%><!-- resolve variables --> <%--@elvariable id='timesheets' type='java.util.List<org.timesheet.domain.Timesheet>'--%><html> <head><title>Timesheets</title><link rel='stylesheet' href='/timesheet-app/resources/style.css' type='text/css'> </head> <body><h1>List of timesheets</h1><a href='timesheets?new'>Add new timesheet</a><table cellspacing='5' class='main-table wide'><tr><th style='width: 30%'>Employee</th><th style='width: 50%'>Task</th><th>Hours</th><th>Details</th><th>Delete</th></tr><c:forEach items='${timesheets}' var='ts'><tr><td><a href='employees/${ts.who.id}'>${ts.who.name}</a></td><td><a href='tasks/${ts.task.id}'>${ts.task.description}</a></td><td>${ts.hours}</td><td><a href='timesheets/${ts.id}'>Go to page</a></td><td><sf:form action='timesheets/${ts.id}' method='delete' cssClass='delete'><input type='submit' class='delete-button'></sf:form></td></tr></c:forEach></table><br /><a href='welcome'>Go back</a> </body> </html>

刪除時(shí)間表比刪除任務(wù)更容易,因?yàn)槲覀儾粫?huì)破壞數(shù)據(jù)庫中的任何約束,因此我們可以在DAO上使用默認(rèn)的remove方法:

/*** Deletes timeshet with specified ID* @param id Timesheet's ID* @return redirects to timesheets*/@RequestMapping(value = '/{id}', method = RequestMethod.DELETE)public String deleteTimesheet(@PathVariable('id') long id) {Timesheet toDelete = timesheetDao.find(id);timesheetDao.remove(toDelete);return 'redirect:/timesheets';}

我們將照常通過將其ID添加到URI來訪問單個(gè)時(shí)間表資源,因此我們將處理/ timesheets / {id}。 但是有分配給時(shí)間表的對(duì)象-任務(wù)實(shí)例和員工實(shí)例。 我們不希望表單將其無效。 因此,我們將為表單引入輕量級(jí)的命令支持對(duì)象。 我們將只更新小時(shí),然后在實(shí)際的時(shí)間表實(shí)例中設(shè)置這些新小時(shí):

/*** Returns timesheet with specified ID* @param id Timesheet's ID* @param model Model to put timesheet to* @return timesheets/view*/@RequestMapping(value = '/{id}', method = RequestMethod.GET)public String getTimesheet(@PathVariable('id') long id, Model model) {Timesheet timesheet = timesheetDao.find(id);TimesheetCommand tsCommand = new TimesheetCommand(timesheet);model.addAttribute('tsCommand', tsCommand);return 'timesheets/view';}

這是TimesheetCommand的代碼,現(xiàn)在位于新包org.timesheet.web下。 命令

package org.timesheet.web.commands;import org.hibernate.validator.constraints.Range; import org.timesheet.domain.Timesheet;import javax.validation.constraints.NotNull;public class TimesheetCommand {@NotNull@Range(min = 1, message = 'Hours must be 1 or greater')private Integer hours;private Timesheet timesheet;// default c-tor for bean instantiationpublic TimesheetCommand() {}public TimesheetCommand(Timesheet timesheet) {hours = timesheet.getHours();this.timesheet = timesheet;}public Integer getHours() {return hours;}public void setHours(Integer hours) {this.hours = hours;}public Timesheet getTimesheet() {return timesheet;}public void setTimesheet(Timesheet timesheet) {this.timesheet = timesheet;}@Overridepublic boolean equals(Object o) {if (this == o) {return true;}if (o == null || getClass() != o.getClass()) {return false;}TimesheetCommand that = (TimesheetCommand) o;if (hours != null ? !hours.equals(that.hours) : that.hours != null) {return false;}if (timesheet != null ? !timesheet.equals(that.timesheet) : that.timesheet != null) {return false;}return true;}@Overridepublic int hashCode() {int result = hours != null ? hours.hashCode() : 0;result = 31 * result + (timesheet != null ? timesheet.hashCode() : 0);return result;} }

很簡(jiǎn)單,但是@NotNull@Range注釋是什么? 好吧,我們絕對(duì)不希望用戶輸入小時(shí)數(shù)為負(fù)數(shù)或零,因此我們將使用此簡(jiǎn)潔的JSR 303 Bean驗(yàn)證API。 要使其工作,只需將對(duì)Hibernate驗(yàn)證器的依賴項(xiàng)添加到pom.xml中

<dependency><groupId>org.hibernate</groupId><artifactId>hibernate-validator</artifactId><version>4.2.0.Final</version></dependency>

將Hibernate Validator放入我們的類路徑后,將自動(dòng)選擇默認(rèn)的驗(yàn)證器。 為了使其工作,我們必須啟用注釋驅(qū)動(dòng)的MVC,因此將以下行添加到timesheet-servlet.xml bean配置文件中:

<mvc:annotation-driven />

我們將在幾行后看到有效模型的用法。

現(xiàn)在,在時(shí)間表文件夾下,我們將創(chuàng)建view.jsp頁面,其中包含有關(guān)單個(gè)時(shí)間表的信息:

<%@ page contentType='text/html;charset=UTF-8' language='java' %> <%@ taglib prefix='sf' uri='http://www.springframework.org/tags/form'%> <%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%><%--@elvariable id='tsCommand' type='org.timesheet.web.commands.TimesheetCommand'--%><html> <head><title>Timesheet page</title><link rel='stylesheet' href='/timesheet-app/resources/style.css' type='text/css'> </head> <body><h2>Timesheet info</h2><div id='list'><sf:form method='post' modelAttribute='tsCommand'><sf:errors path='*' cssClass='errors' element='div' /><ul><li><label for='employeeName'>Assigned employee:</label><a id='employee' href='../employees/${tsCommand.timesheet.who.id}'>${tsCommand.timesheet.who.name}</a></li><li><label for='task'>Task:</label><a id='task' href='../tasks/${tsCommand.timesheet.task.id}'>${tsCommand.timesheet.task.description}</a></li><li><label for='hours'>Hours:</label><input name='hours' id='hours' value='${tsCommand.hours}' /></li><li><input type='submit' value='Save' /></li></ul></sf:form></div><br /><br /><a href='../timesheets'>Go Back</a> </body> </html>

在此視圖頁面中,我們具有提交按鈕,該按鈕將觸發(fā)/ timesheets / {id}上的POST請(qǐng)求并傳遞更新的模型(該模型中的TimesheetCommand實(shí)例)。 因此,讓我們處理一下。 我們將使用@Valid批注,它是JSR 303 Bean驗(yàn)證API的一部分,用于標(biāo)記要驗(yàn)證的對(duì)象。 另請(qǐng)注意,必須使用@ModelAttribute批注對(duì)TimesheetCommand進(jìn)行批注,因?yàn)榇嗣钜呀壎ǖ絎eb視圖。 驗(yàn)證錯(cuò)誤存儲(chǔ)在BindingResult對(duì)象中:

/*** Updates timesheet with given ID* @param id ID of timesheet to lookup from DB* @param tsCommand Lightweight command object with changed hours* @return redirects to timesheets*/@RequestMapping(value = '/{id}', method = RequestMethod.POST)public String updateTimesheet(@PathVariable('id') long id,@Valid @ModelAttribute('tsCommand') TimesheetCommand tsCommand,BindingResult result) {Timesheet timesheet = timesheetDao.find(id);if (result.hasErrors()) {tsCommand.setTimesheet(timesheet);return 'timesheets/view';}// no errors, update timesheettimesheet.setHours(tsCommand.getHours());timesheetDao.update(timesheet);return 'redirect:/timesheets';}

要進(jìn)行添加,我們必須從現(xiàn)有任務(wù)和員工的選擇菜單中進(jìn)行選擇,因此在提供新表單時(shí),我們將傳遞這些列表:

/*** Creates form for new timesheet* @param model Model to bind to HTML form* @return timesheets/new*/@RequestMapping(params = 'new', method = RequestMethod.GET)public String createTimesheetForm(Model model) {model.addAttribute('timesheet', new Timesheet());model.addAttribute('tasks', taskDao.list());model.addAttribute('employees', employeeDao.list());return 'timesheets/new';}

為了顯示員工和任務(wù)的選擇列表,我們?cè)俅涡枰獮槠鋭?chuàng)建編輯器。 我們之前已經(jīng)看到了這種方法,因此像以前一樣,我們向我們的項(xiàng)目中添加2個(gè)使用相應(yīng)DAO的新編輯器:

package org.timesheet.web.editors;import org.timesheet.domain.Employee; import org.timesheet.service.dao.EmployeeDao;import java.beans.PropertyEditorSupport;/*** Will convert ID from combobox to employee's instance.*/ public class EmployeeEditor extends PropertyEditorSupport {private EmployeeDao employeeDao;public EmployeeEditor(EmployeeDao employeeDao) {this.employeeDao = employeeDao;}@Overridepublic void setAsText(String text) throws IllegalArgumentException {long id = Long.parseLong(text);Employee employee = employeeDao.find(id);setValue(employee);} }package org.timesheet.web.editors;import org.timesheet.domain.Task; import org.timesheet.service.dao.TaskDao;import java.beans.PropertyEditorSupport;public class TaskEditor extends PropertyEditorSupport {private TaskDao taskDao;public TaskEditor(TaskDao taskDao) {this.taskDao = taskDao;}@Overridepublic void setAsText(String text) throws IllegalArgumentException {long id = Long.parseLong(text);Task task = taskDao.find(id);setValue(task);} }

我們將在TimesheetController initBinder方法中注冊(cè)這些編輯器:

@InitBinderprotected void initBinder(WebDataBinder binder) {binder.registerCustomEditor(Employee.class, new EmployeeEditor(employeeDao));binder.registerCustomEditor(Task.class, new TaskEditor(taskDao));}

現(xiàn)在,我們可以安全地在timesheets文件夾下添加new.jsp ,因?yàn)檫x擇列表將正確地填充有模型中傳遞的數(shù)據(jù):

<%@ taglib prefix='sf' uri='http://www.springframework.org/tags/form' %> <%@ page contentType='text/html;charset=UTF-8' language='java' %><%--@elvariable id='employees' type='java.util.List<org.timesheet.domain.Employee'--%> <%--@elvariable id='tasks' type='java.util.List<org.timesheet.domain.Task'--%><html> <head><title>Add new timesheet</title><link rel='stylesheet' href='/timesheet-app/resources/style.css' type='text/css'> </head> <body><h2>Add new Timesheet</h2><div id='list'><sf:form method='post' action='timesheets' commandName='timesheet'><ul><li><label for='employees'>Pick employee:</label><sf:select path='who' id='employees'><sf:options items='${employees}' itemLabel='name' itemValue='id' /></sf:select></li><li><label for='tasks'>Pick task:</label><sf:select path='task' id='tasks'><sf:options items='${tasks}' itemLabel='description' itemValue='id' /></sf:select></li><li><label for='hours'>Hours:</label><sf:input path='hours' /></li><li><input type='submit' value='Save' /></li></ul></sf:form></div><br /><br /><a href='timesheets'>Go Back</a> </body> </html>

Submit按鈕在/ timesheets路徑上提交POST請(qǐng)求,因此我們將使用非常簡(jiǎn)單的控制器方法來處理此請(qǐng)求:

/*** Saves new Timesheet to the database* @param timesheet Timesheet to save* @return redirects to timesheets*/@RequestMapping(method = RequestMethod.POST)public String addTimesheet(Timesheet timesheet) {timesheetDao.add(timesheet);return 'redirect:/timesheets';}

因此,所有時(shí)間表功能現(xiàn)在都應(yīng)該可以正常工作,只需確保使用應(yīng)用程序一段時(shí)間即可。 當(dāng)然,我們現(xiàn)在還將為TimesheetController編寫單元測(cè)試。 在測(cè)試方法testUpdateTimesheetValid和testUpdateTimesheetInValid中,我們不是手動(dòng)驗(yàn)證對(duì)象,而是模擬驗(yàn)證器:

package org.timesheet.web;import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.ui.ExtendedModelMap; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.timesheet.DomainAwareBase; import org.timesheet.domain.Employee; import org.timesheet.domain.Manager; import org.timesheet.domain.Task; import org.timesheet.domain.Timesheet; import org.timesheet.service.dao.EmployeeDao; import org.timesheet.service.dao.ManagerDao; import org.timesheet.service.dao.TaskDao; import org.timesheet.service.dao.TimesheetDao; import org.timesheet.web.commands.TimesheetCommand;import java.util.Collection; import java.util.List;import static org.junit.Assert.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when;@ContextConfiguration(locations = {'/persistence-beans.xml', '/controllers.xml'}) public class TimesheetControllerTest extends DomainAwareBase {@Autowiredprivate TimesheetDao timesheetDao;@Autowiredprivate EmployeeDao employeeDao;@Autowiredprivate ManagerDao managerDao;@Autowiredprivate TaskDao taskDao;@Autowiredprivate TimesheetController controller;private Model model; // used for controller@Beforepublic void setUp() {model = new ExtendedModelMap();}@Testpublic void testShowTimesheets() {// prepare some dataTimesheet timesheet = sampleTimesheet();// use controllerString view = controller.showTimesheets(model);assertEquals('timesheets/list', view);List<Timesheet> listFromDao = timesheetDao.list();Collection<?> listFromModel = (Collection<?>) model.asMap().get('timesheets');assertTrue(listFromModel.contains(timesheet));assertTrue(listFromDao.containsAll(listFromModel));}@Testpublic void testDeleteTimesheet() {// prepare ID to deleteTimesheet timesheet = sampleTimesheet();timesheetDao.add(timesheet);long id = timesheet.getId();// delete & assertString view = controller.deleteTimesheet(id);assertEquals('redirect:/timesheets', view);assertNull(timesheetDao.find(id));}@Testpublic void testGetTimesheet() {// prepare timesheetTimesheet timesheet = sampleTimesheet();timesheetDao.add(timesheet);long id = timesheet.getId();TimesheetCommand tsCommand = new TimesheetCommand(timesheet);// get & assertString view = controller.getTimesheet(id, model);assertEquals('timesheets/view', view);assertEquals(tsCommand, model.asMap().get('tsCommand'));}@Testpublic void testUpdateTimesheetValid() {// prepare ID to deleteTimesheet timesheet = sampleTimesheet();timesheetDao.add(timesheet);long id = timesheet.getId();TimesheetCommand tsCommand = new TimesheetCommand(timesheet);// user alters Timesheet hours in HTML form with valid valuetsCommand.setHours(1337);BindingResult result = mock(BindingResult.class);when(result.hasErrors()).thenReturn(false);// update & assertString view = controller.updateTimesheet(id, tsCommand, result);assertEquals('redirect:/timesheets', view);assertTrue(1337 == timesheetDao.find(id).getHours());}@Testpublic void testUpdateTimesheetInValid() {// prepare ID to deleteTimesheet timesheet = sampleTimesheet();timesheetDao.add(timesheet);long id = timesheet.getId();TimesheetCommand tsCommand = new TimesheetCommand(timesheet);Integer originalHours = tsCommand.getHours();// user alters Timesheet hours in HTML form with valid valuetsCommand.setHours(-1);BindingResult result = mock(BindingResult.class);when(result.hasErrors()).thenReturn(true);// update & assertString view = controller.updateTimesheet(id, tsCommand, result);assertEquals('timesheets/view', view);assertEquals(originalHours, timesheetDao.find(id).getHours());}@Testpublic void testAddTimesheet() {// prepare timesheetTimesheet timesheet = sampleTimesheet();// save but via controllerString view = controller.addTimesheet(timesheet);assertEquals('redirect:/timesheets', view);// timesheet is stored in DBassertEquals(timesheet, timesheetDao.find(timesheet.getId()));}private Timesheet sampleTimesheet() {Employee marty = new Employee('Martin Brodeur', 'NHL');employeeDao.add(marty);Manager jeremy = new Manager('Jeremy');managerDao.add(jeremy);Task winStanleyCup = new Task('NHL finals', jeremy, marty);taskDao.add(winStanleyCup);Timesheet stanelyCupSheet = new Timesheet(marty, winStanleyCup, 100);timesheetDao.add(stanelyCupSheet);return stanelyCupSheet;} }

我們要做的最后一個(gè)控制器是針對(duì)我們的特殊業(yè)務(wù)服務(wù)– TimesheetService。 我們已經(jīng)實(shí)現(xiàn)并測(cè)試了它的邏輯。 Controller會(huì)將這些功能簡(jiǎn)單地合并到一個(gè)菜單頁面,我們將使用Controller處理每個(gè)功能。 因此,讓我們首先添加一些樣板控制器定義和DAO布線:

@Controller @RequestMapping('/timesheet-service') public class TimesheetServiceController {private TimesheetService service;private EmployeeDao employeeDao;private ManagerDao managerDao;@Autowiredpublic void setService(TimesheetService service) {this.service = service;}@Autowiredpublic void setEmployeeDao(EmployeeDao employeeDao) {this.employeeDao = employeeDao;}@Autowiredpublic void setManagerDao(ManagerDao managerDao) {this.managerDao = managerDao;}}

當(dāng)用戶使用GET請(qǐng)求進(jìn)入/ timesheet-service時(shí),我們將使用填充的數(shù)據(jù)為他提供菜單服務(wù):

/*** Shows menu of timesheet service: * that contains busiest task and employees and managers to* look for their assigned tasks.* @param model Model to put data to* @return timesheet-service/list*/@RequestMapping(method = RequestMethod.GET)public String showMenu(Model model) {model.addAttribute('busiestTask', service.busiestTask());model.addAttribute('employees', employeeDao.list());model.addAttribute('managers', managerDao.list());return 'timesheet-service/menu';}

再次,為了使內(nèi)容在選擇列表中起作用,我們將注冊(cè)編輯器(我們將僅重用最近創(chuàng)建的編輯器):

@InitBinderprotected void initBinder(WebDataBinder binder) {binder.registerCustomEditor(Employee.class, new EmployeeEditor(employeeDao));binder.registerCustomEditor(Manager.class, new ManagerEditor(managerDao));}

現(xiàn)在我們將提供服務(wù)。 我們將再次擁有RESTful URL,但是實(shí)際資源不會(huì)像以前那樣直接映射到域模型,而是一些內(nèi)部服務(wù)的結(jié)果。 因此,獲取ID為123的經(jīng)理的任務(wù)將導(dǎo)致GET請(qǐng)求時(shí)間表/ manager-tasks / 123。 員工任務(wù)相同。 我們將使用選擇列表的偵聽器與jQuery形成實(shí)際的URL。 添加時(shí)間表服務(wù)文件夾,并在其中添加menu.jsp頁面,其內(nèi)容如下:

<%@ taglib prefix='sf' uri='http://www.springframework.org/tags/form' %> <%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core' %> <%@ page contentType='text/html;charset=UTF-8' language='java' %><%--@elvariable id='busiestTask' type='org.timesheet.domain.Task'--%> <%--@elvariable id='managers' type='java.util.List<org.timesheet.domain.Manager>'--%> <%--@elvariable id='employees' type='java.util.List<org.timesheet.domain.Employee>'--%><html> <head><title>Timesheet Service</title><link rel='stylesheet' href='/timesheet-app/resources/style.css' type='text/css'> </head> <body><h1>Timesheet services</h1><div id='list'><h3>Busiest task</h3><ul><li><a href='/timesheet-app/tasks/${busiestTask.id}'id='busiest-task'>${busiestTask.description}</a></li></ul><h3>Tasks for manager</h3><sf:form method='get' id='manager-form'><ul><li><select id='select-managers'><c:forEach items='${managers}' var='man'><option value='${man.id}'>${man.name}</option></c:forEach></select></li><li><input type='submit' value='Search' /></li></ul></sf:form><h3>Tasks for employee</h3><sf:form method='get' id='employee-form'><ul><li><select id='select-employees'><c:forEach items='${employees}' var='emp'><option value='${emp.id}'>${emp.name}</option></c:forEach></select></li><li><input type='submit' value='Search'></li></ul></sf:form></div><br /><br /><a href='/timesheet-app/welcome'>Go Back</a><script src='/timesheet-app/resources/jquery-1.7.1.js'></script><script type='text/javascript'>(function() {// set default actionssetAddAction('#select-managers', '#manager-form', 'manager-tasks');setAddAction('#select-employees', '#employee-form', 'employee-tasks');// handler for chaning action$('#select-managers').on('change', function() {setAddAction('#select-managers', '#manager-form', 'manager-tasks');});$('#select-employees').on('change', function() {setAddAction('#select-employees', '#employee-form', 'employee-tasks');});function setAddAction(selectName, formName, action) {var id = $(selectName).val();$(formName).attr('action','/timesheet-app/timesheet-service/' + action + '/' + id);}})();</script> </body> </html>

獲取給定經(jīng)理的任務(wù):

/*** Returns tasks for given manager* @param id ID of manager* @param model Model to put tasks and manager* @return timesheet-service/manager-tasks*/@RequestMapping(value = '/manager-tasks/{id}', method = RequestMethod.GET)public String showManagerTasks(@PathVariable('id') long id, Model model) {Manager manager = managerDao.find(id);List<Task> tasks = service.tasksForManager(manager);model.addAttribute('manager', manager);model.addAttribute('tasks', tasks);return 'timesheet-service/manager-tasks';}

結(jié)果頁面timesheet -service / manager-tasks.jsp將呈現(xiàn):

<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core' %> <%@ page contentType='text/html;charset=UTF-8' language='java' %><%--@elvariable id='manager' type='org.timesheet.domain.Manager'--%> <%--@elvariable id='tasks' type='java.util.List<org.timesheet.domain.Task>'--%><html> <head><title>Tasks for manager</title><link rel='stylesheet' href='/timesheet-app/resources/style.css' type='text/css'> </head> <body><h3>Current manager: <a href='/timesheet-app/managers/${manager.id}'>${manager.name}</a></h3><div id='list'><c:forEach items='${tasks}' var='task'><li><a href='/timesheet-app/tasks/${task.id}'>${task.description}</a></li></c:forEach></div><br /><br /><a href='../'>Go Back</a> </body> </html>

我們將為員工做幾乎相同的事情:

/*** Returns tasks for given employee* @param id ID of employee* @param model Model to put tasks and employee* @return timesheet-service/employee-tasks*/@RequestMapping(value = '/employee-tasks/{id}', method = RequestMethod.GET)public String showEmployeeTasks(@PathVariable('id') long id, Model model) {Employee employee = employeeDao.find(id);List<Task> tasks = service.tasksForEmployee(employee);model.addAttribute('employee', employee);model.addAttribute('tasks', tasks);return 'timesheet-service/employee-tasks';}

然后用jsp查看employee-tasks.jsp:

<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core' %> <%@ page contentType='text/html;charset=UTF-8' language='java' %><%--@elvariable id='employee' type='org.timesheet.domain.Employee'--%> <%--@elvariable id='tasks' type='java.util.List<org.timesheet.domain.Task>'--%><html> <head><title>Tasks for employee</title><link rel='stylesheet' href='/timesheet-app/resources/style.css' type='text/css'> </head> <body><h3>Current employee: <a href='/timesheet-app/employees/${employee.id}'>${employee.name}</a></h3><div id='list'><c:forEach items='${tasks}' var='task'><li><a href='/timesheet-app/tasks/${task.id}'>${task.description}</a></li></c:forEach></div><br /><br /><a href='../'>Go Back</a> </body> </html>

因此,請(qǐng)確保一切都集成良好,并為此新contoller添加單元測(cè)試:

package org.timesheet.web;import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.FileSystemResource; import org.springframework.jdbc.core.simple.SimpleJdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.jdbc.SimpleJdbcTestUtils; import org.springframework.ui.ExtendedModelMap; import org.springframework.ui.Model; import org.timesheet.DomainAwareBase; import org.timesheet.domain.Employee; import org.timesheet.domain.Manager; import org.timesheet.service.TimesheetService; import org.timesheet.service.dao.EmployeeDao; import org.timesheet.service.dao.ManagerDao;import static org.junit.Assert.assertEquals;/*** This test relies on fact that DAOs and Services are tested individually.* Only compares, if controller returns the same as individual services.*/ @ContextConfiguration(locations = {'/persistence-beans.xml', '/controllers.xml'}) public class TimesheetServiceControllerTest extends DomainAwareBase {@Autowiredprivate TimesheetServiceController controller;@Autowiredprivate TimesheetService timesheetService;@Autowiredprivate EmployeeDao employeeDao;@Autowiredprivate ManagerDao managerDao;@Autowiredprivate SimpleJdbcTemplate jdbcTemplate;private Model model;private final String createScript = 'src/main/resources/sql/create-data.sql';@Beforepublic void setUp() {model = new ExtendedModelMap();SimpleJdbcTestUtils.executeSqlScript(jdbcTemplate,new FileSystemResource(createScript), false);}@Testpublic void testShowMenu() {String view = controller.showMenu(model);assertEquals('timesheet-service/menu', view);assertEquals(timesheetService.busiestTask(),model.asMap().get('busiestTask'));// this should be done only on small data sample// might cause serious performance cost for completeassertEquals(employeeDao.list(), model.asMap().get('employees'));assertEquals(managerDao.list(), model.asMap().get('managers'));}@Testpublic void testShowManagerTasks() {// prepare some IDManager manager = managerDao.list().get(0);long id = manager.getId();String view = controller.showManagerTasks(id, model);assertEquals('timesheet-service/manager-tasks', view);assertEquals(manager, model.asMap().get('manager'));assertEquals(timesheetService.tasksForManager(manager),model.asMap().get('tasks'));}@Testpublic void testShowEmployeeTasks() {// prepare some IDEmployee employee = employeeDao.list().get(0);long id = employee.getId();String view = controller.showEmployeeTasks(id, model);assertEquals('timesheet-service/employee-tasks', view);assertEquals(employee, model.asMap().get('employee'));assertEquals(timesheetService.tasksForEmployee(employee),model.asMap().get('tasks'));} }

這部分之后的項(xiàng)目結(jié)構(gòu)(所有新內(nèi)容都可見):

最終請(qǐng)求映射:

參考: 第5部分–在vrtoonjava博客上從我們的JCG合作伙伴 Michal Vrtiak 添加Spring MVC第2部分 。


翻譯自: https://www.javacodegeeks.com/2012/09/spring-adding-spring-mvc-part-2.html

總結(jié)

以上是生活随笔為你收集整理的Spring –添加Spring MVC –第2部分的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

日韩电影久久 | 国产精品毛片一区视频播不卡 | 日韩精品在线观看av | 日韩av在线高清 | 国产裸体bbb视频 | av网站在线免费观看 | 中文字幕在线观看你懂的 | 国产黄a三级三级三级三级三级 | av最新资源 | 日韩在线视频观看免费 | 综合婷婷丁香 | 成全在线视频免费观看 | 婷婷国产一区二区三区 | 亚州av成人| 精品免费国产一区二区三区四区 | 天天干天天碰 | 日韩深夜在线观看 | 亚洲专区在线 | 狠狠的操狠狠的干 | 天天干,天天干 | 国产午夜不卡 | 91丨九色丨勾搭 | 五月婷婷丁香综合 | 三级a毛片| www.神马久久 | 久久久久北条麻妃免费看 | 黄色小视频在线观看免费 | 久草在线资源免费 | 91插插视频| 女人魂免费观看 | 成人高清在线观看 | 国产高清不卡 | 五月天免费网站 | 婷婷丁香激情综合 | 亚洲狠狠干 | 久久国产精品久久精品 | 婷婷综合导航 | 性色av免费在线观看 | 操老逼免费视频 | 在线97| 国产美女永久免费 | 国产精品久久久久久久久久久免费 | 欧美日韩性视频 | 久久视频在线观看免费 | www久久精品| av免费黄色 | 日韩免费网站 | 日韩黄色免费看 | 国产美女免费视频 | 亚洲免费精品视频 | 美女黄频网站 | 亚洲精品在线观看的 | 99久久婷婷国产综合精品 | 国产精品视频 | 亚洲精选99 | 国产成人精品国内自产拍免费看 | 毛片播放网站 | 美女视频一区二区 | 亚洲成人精品国产 | 亚洲a在线观看 | 97免费视频在线 | 国产伦理一区二区 | 国产高清成人 | 91人人爱 | 成人黄视频 | 美女视频黄在线 | 亚洲蜜桃av | 久久精品99国产精品亚洲最刺激 | 91av视频在线观看免费 | 欧美日韩国产在线 | 国产精品九九九九九 | 亚洲日本在线一区 | 国产成人精品综合久久久久99 | 国产一二区精品 | www91在线 | 免费观看日韩av | 久久久国产日韩 | а天堂中文最新一区二区三区 | 波多野结衣在线播放一区 | 免费一级片观看 | 激情影音先锋 | 国产成人精品免高潮在线观看 | 在线小视频你懂得 | 91最新网址在线观看 | 国产精品一区久久久久 | 国产丝袜一区二区三区 | 亚洲综合精品在线 | 婷婷六月色 | av一级在线 | 91精品国自产在线观看欧美 | 国产午夜免费视频 | 欧美日韩高清免费 | 精品视频国产一区 | 久久午夜免费观看 | 精品视频资源站 | 亚洲精品乱码久久久久久蜜桃不爽 | 午夜精品久久 | 成人午夜电影在线 | 久久久久 免费视频 | 国产日韩欧美中文 | 天天亚洲综合 | 精品一区二区在线免费观看 | 最近高清中文字幕在线国语5 | 日本精品视频在线 | 夜夜夜草 | 91精品视频免费观看 | 色综合天天综合 | 久久人人爽爽人人爽人人片av | 在线观看理论 | 国产综合91 | 国产精品国产三级国产不产一地 | 成人在线播放av | 免费日韩在线 | 免费在线一区二区三区 | 国产伦精品一区二区三区四区视频 | 国产黄色片一级三级 | 六月丁香色婷婷 | 五月天.com| 国产精品欧美激情在线观看 | 亚洲精品男人的天堂 | 久久男人免费视频 | 欧美精品少妇xxxxx喷水 | 高清不卡毛片 | 国产精品初高中精品久久 | 玖玖精品在线 | 亚洲国产精品成人精品 | 免费在线日韩 | 黄色小说在线免费观看 | 丁香花五月 | 在线观看免费 | 中文在线免费观看 | 91亚州| 亚洲激情小视频 | 夜夜爽夜夜操 | 日日爽天天操 | 草免费视频 | 欧美在线一| 麻豆视频在线播放 | 欧美成人免费在线 | 美女福利视频一区二区 | 色婷婷导航 | 91在线播 | 国产精品久久电影观看 | 国产精品毛片一区视频 | 久草免费手机视频 | 免费三级a| 天天射天 | 在线免费观看视频一区二区三区 | 福利视频一二区 | 在线观看成人 | 精品xxx | 精品综合久久 | 少妇视频在线播放 | 欧美精品一区在线发布 | 四虎影视成人永久免费观看亚洲欧美 | 国产一区自拍视频 | 亚洲黄色在线观看 | 亚洲伦理电影在线 | 中文av在线免费观看 | 视频二区在线 | 香蕉视频网站在线观看 | 一区二区三区中文字幕在线 | 欧美成人在线免费观看 | a色视频 | 亚洲成人免费在线 | 丁香六月五月婷婷 | 久久久久久欧美二区电影网 | 黄色三级视频片 | 国产香蕉久久精品综合网 | 欧美精品xx | 女人18毛片a级毛片一区二区 | 九九精品视频在线观看 | 午夜久久久久久久久 | 尤物九九久久国产精品的分类 | 91丨九色丨国产丨porny精品 | 久久国色夜色精品国产 | 99热这里只有精品在线观看 | 欧美一级性 | 国产一级高清视频 | 99热在 | 欧美极品少妇xxxxⅹ欧美极品少妇xxxx亚洲精品 | 日韩欧美视频免费观看 | 久久久人 | 国产黄网在线 | 久久久久久99精品 | 亚洲国产视频在线 | 国产精品久久网 | 一区二区三区免费在线 | 国产黄影院色大全免费 | 在线 视频 一区二区 | 久久精品理论 | av色网站| 国产福利中文字幕 | 91麻豆精品91久久久久同性 | 激情影院在线观看 | 91精品视频网站 | 久久97久久 | 一区二区三区免费 | 日韩二区三区在线 | 国产色视频一区二区三区qq号 | 成人黄在线 | 91精品视频在线观看免费 | 天天操夜夜操夜夜操 | 黄色大片日本 | 国产日韩一区在线 | 成人av网页| 91精品国产乱码久久桃 | av免费线看 | 色激情在线 | 久久久久日本精品一区二区三区 | 国产国产人免费人成免费视频 | 亚洲精品国产成人 | 国产99一区 | 色综合a| 天天操狠狠操网站 | 欧美在线视频一区二区三区 | 成 人 免费 黄 色 视频 | 亚洲成av人片一区二区梦乃 | 国产色视频一区二区三区qq号 | 美女视频免费精品 | 综合网在线视频 | 欧美成年人在线视频 | 久久国产精品精品国产色婷婷 | 狠狠综合久久av | 在线看一区| 欧美精品久久 | 91中文字幕在线观看 | 99久久久久免费精品国产 | 免费国产一区二区视频 | 九九热视频在线播放 | 亚洲黄色区 | 欧美成人视 | 97超碰在线资源 | av线上看 | 天天草夜夜 | 国产精品美女www爽爽爽视频 | 丰满少妇久久久 | 97久久精品午夜一区二区 | 一区二区不卡视频在线观看 | 91九色在线| 成人久久电影 | 精品亚洲成a人在线观看 | 97国产精品视频 | 日韩在线视频一区二区三区 | 国产小视频福利在线 | 日日爽天天爽 | 嫩草伊人久久精品少妇av | 久久午夜鲁丝片 | 黄色福利视频网站 | 久久9999久久免费精品国产 | 激情偷乱人伦小说视频在线观看 | 超碰com| 成 人 黄 色 免费播放 | www.久久色| 亚洲精品高清视频在线观看 | 中文在线 | 国产黄色资源 | 亚洲人成人在线 | 日韩av网址在线 | 国产亚洲精品久久久久久移动网络 | 人人爽人人爽人人爽学生一级 | 人人讲 | 国产在线精品播放 | 久久久一本精品99久久精品 | 91精品视频在线观看免费 | 午夜色场| 久久久久久久久精 | 国产免费观看视频 | 婷婷亚洲五月色综合 | 成人久久免费视频 | 亚洲精品午夜国产va久久成人 | 国产精品一区二区免费看 | 最新中文字幕视频 | 九九九电影免费看 | 日本精品视频在线观看 | 成人免费在线播放 | 精品国产成人在线影院 | 亚洲精品国产区 | 亚洲伊人色 | 丁香六月婷 | 亚洲欧美成aⅴ人在线观看 四虎在线观看 | 日本女人b | 免费高清在线一区 | 黄色成人av | www.com在线观看 | 在线免费观看羞羞视频 | 久久久资源网 | 丁香高清视频在线看看 | www.久草.com | 欧美男男激情videos | 亚洲黄色在线播放 | 成人免费大片黄在线播放 | 超碰人人超 | 蜜桃视频日本 | 久久视频国产 | 国际av在线 | 国产美女精品人人做人人爽 | 欧美极品一区二区三区 | 日韩在线视频不卡 | 韩国av一区二区三区在线观看 | 精品黄色在线 | 黄色影院在线观看 | 最新中文字幕在线播放 | 亚洲一区二区精品 | a v在线观看 | 日本公妇在线观看高清 | 免费看日韩| 黄色小说免费观看 | 91中文字幕在线观看 | 91视频下载 | 在线观看完整版免费 | 久久久久国产精品免费网站 | 日日草av | 99精品免费久久久久久久久 | 国产在线污 | 人人澡人人爽欧一区 | 一二三久久久 | 视频成人永久免费视频 | 午夜久久久久久久久久影院 | 国产精品久久久精品 | 亚洲黄色激情小说 | 91视频免费看 | 亚洲国产中文字幕在线 | 日韩欧美在线影院 | 免费看av在线| 视频一区二区精品 | 在线视频国产区 | 亚洲一区av | 国产伦理精品一区二区 | 天堂网中文在线 | 国产专区在线看 | 九九免费精品视频 | 国产成人在线播放 | 久久精品国产免费看久久精品 | 免费看特级毛片 | 久久久久国 | 久久一区二区三区超碰国产精品 | 久久涩涩网站 | 一二三区av | 精品毛片一区二区免费看 | 麻豆国产网站入口 | 美女免费视频观看网站 | a在线观看视频 | 97超碰国产精品女人人人爽 | www.久久99| 精品亚洲欧美无人区乱码 | 久久精品9 | 91在线视频免费观看 | 欧美视频二区 | 十八岁以下禁止观看的1000个网站 | 99麻豆视频 | 亚洲精品视频网址 | 婷婷丁香激情 | 日韩av中文在线观看 | 精品国产精品久久 | 在线观看免费91 | 深爱激情站 | 国内精品久久天天躁人人爽 | 久久久久久久久艹 | 国产精品国产三级国产专区53 | 久久96国产精品久久99软件 | 在线视频 国产 日韩 | 又黄又色又爽 | www免费网站在线观看 | 精品在线观 | 色婷婷激情 | 久久精品爱视频 | 日日干夜夜爱 | 久热爱| 中文字幕免费高 | 日日操日日 | 国产精品成人久久久久 | 精品女同一区二区三区在线观看 | 中文字幕在线成人 | 狠狠干夜夜操 | 欧美激情视频在线观看免费 | 黄色一级免费电影 | 中日韩三级视频 | 五月婷网站 | 在线免费观看视频 | 一区二区三区 中文字幕 | 亚洲视频免费视频 | 91最新在线观看 | 婷婷丁香七月 | 99久久9| 欧美激情视频一区二区三区免费 | 在线 视频 一区二区 | 国产一区二区久久久久 | 日韩丝袜视频 | 亚洲精品视频免费在线 | 丁香5月婷婷 | va视频在线 | 久久精品国产美女 | 国产精品久久久久久久久蜜臀 | 久久综合久久综合久久综合 | 国产在线不卡 | 欧美亚洲一级片 | www四虎影院| 欧美大香线蕉线伊人久久 | 久草在线免费在线观看 | 天天搞天天| 三级黄色免费片 | 日韩在线视频一区二区三区 | 国产精品一区二区三区久久 | 国产综合在线视频 | 天天摸天天操天天爽 | 日韩动态视频 | 最新日韩在线观看 | 久久久久久久国产精品视频 | 亚洲 成人 欧美 | 97热久久免费频精品99 | 欧美aaa一级 | 免费在线观看不卡av | 精品免费一区二区三区 | 成人9ⅰ免费影视网站 | 国产精品综合久久久久久 | 国产精品永久 | 日韩 国产| 日韩久久久久久久久久久久 | 免费特级黄毛片 | 狠狠色丁香久久婷婷综 | 欧美精品亚洲精品日韩精品 | 911亚洲精品第一 | 一区二区三区免费在线播放 | 免费看片日韩 | 色99色 | 毛片99| 超碰av在线 | 欧美日韩一区二区视频在线观看 | 91高清免费观看 | 午夜视频在线观看一区二区 | 国产精品字幕 | 欧美国产一区二区 | 日本久久久精品视频 | 97色婷婷| 丁香视频免费观看 | 中文字幕日韩电影 | 亚洲国产婷婷 | 伊人天堂网 | 在线精品视频免费播放 | 91免费看片黄| 久久精品在线免费观看 | 中文超碰字幕 | 欧美污网站 | 亚洲电影在线看 | 黄色小说免费观看 | 久久综合久久综合久久综合 | 4438全国亚洲精品在线观看视频 | 久久国内精品视频 | 久久久首页 | 国产亚洲激情视频在线 | 欧美性黄网官网 | 久久免费久久 | 国产美女在线精品免费观看 | 青青草国产精品视频 | 国产小视频福利在线 | 国产精品一级在线 | 蜜臀久久99精品久久久无需会员 | 96精品高清视频在线观看软件特色 | 在线黄网站| 久久不射影院 | 国产精品av电影 | 日本xxxx裸体xxxx17 | 四虎影视成人永久免费观看视频 | 亚洲欧美视频 | 国产亚洲精品福利 | 黄色一及电影 | 99re久久精品国产 | 麻豆激情电影 | 黄色网址a | 亚洲,国产成人av | 国内成人精品2018免费看 | 日韩最新av | 国产福利中文字幕 | 在线有码中文 | 999男人的天堂 | 在线视频免费观看 | av色网站| 亚洲一级在线观看 | 黄色片免费看 | 日韩欧美综合视频 | av在线电影播放 | 久久久久日本精品一区二区三区 | 欧美成人69av | 中文字幕免费播放 | 视频在线国产 | 蜜臀av免费一区二区三区 | 香蕉视频日本 | 最近中文字幕在线播放 | 免费又黄又爽视频 | 久久综合操| 久久福利综合 | 色婷婷一区| 色视频在线 | 色网站免费在线观看 | 国产片网站 | 国产分类视频 | 狠狠色丁香婷婷综合橹88 | 久久午夜鲁丝片 | 成人看片 | 国产一区二区在线看 | 亚洲高清在线精品 | 在线观看视频99 | av女优中文字幕在线观看 | 欧美大香线蕉线伊人久久 | 激情欧美日韩一区二区 | 99av在线视频 | 午夜精品久久久久久久99无限制 | 欧美 日韩 国产 成人 在线 | 欧美极品少妇xxxxⅹ欧美极品少妇xxxx亚洲精品 | av天天在线观看 | 亚洲精品国偷自产在线91正片 | 久久影院午夜论 | 97在线播放视频 | 久久一久久 | 国产精品一区专区欧美日韩 | 亚洲久草在线视频 | 欧日韩在线视频 | 在线观看视频一区二区三区 | 午夜视频在线观看一区二区三区 | av网站在线观看免费 | 国产亚洲精品久久久久久 | 黄色成人91 | 日韩精品视频免费专区在线播放 | www.av中文字幕.com | 久久视频精品 | 天天色 天天| 91热在线 | 精品国产一区二区三区久久久蜜臀 | 新版资源中文在线观看 | 午夜精品久久久久久久爽 | 国产成人久久精品77777综合 | 日韩一三区 | av在线播放观看 | 久久一级电影 | 中文字幕视频播放 | 一区二区三区在线视频观看58 | 欧美国产三区 | 国产粉嫩在线观看 | 免费裸体视频网 | 91九色在线观看视频 | 99视频国产精品免费观看 | 免费看的黄网站 | 免费a视频在线 | 日韩激情视频在线观看 | 激情欧美一区二区免费视频 | 日韩在线字幕 | 黄色成人影视 | 日日综合 | 在线国产黄色 | 日韩簧片在线观看 | 久草精品免费 | 国产精品视频在线看 | 97电影网手机版 | 国产一区二区三区免费观看视频 | 国产青青青 | 99激情网| 日韩精品观看 | 日韩精品视频免费专区在线播放 | 国产视频 亚洲视频 | 日本久久成人中文字幕电影 | 亚洲精品影视在线观看 | 欧美超碰在线 | 久久精品草 | 黄色资源在线观看 | 国产黄色片一级 | 日本电影黄色 | 91污在线观看 | 五月在线视频 | 国产精品99免视看9 国产精品毛片一区视频 | 国产精选在线观看 | 99精品热视频只有精品10 | 免费在线视频一区二区 | 国产精品电影在线 | 久久久国产成人 | 69视频在线 | 91麻豆精品91久久久久同性 | 欧美综合色 | 欧美一区二区在线 | 日韩久久久久久久久久久久 | 在线观看黄色 | 亚洲欧美国产精品 | 亚洲高清在线精品 | 久久久久久久久免费 | 亚洲乱码精品久久久久 | 日韩一区二区三区观看 | 午夜电影久久 | 国产精品少妇 | 天天爱天天插 | 麻豆 videos| 国产成人三级在线 | 国产性xxxx| 久久热首页 | 久久久久国产精品免费 | 五月开心色 | 曰本免费av | 久草视频免费在线播放 | 国产在线国产 | 三日本三级少妇三级99 | 69中文字幕| 香蕉视频在线看 | 国产精品久久电影观看 | 4438全国亚洲精品观看视频 | 91社区国产高清 | av中文字幕在线看 | 欧美一级电影免费观看 | 激情深爱五月 | 亚洲黄色av | 久久综合五月婷婷 | 九九九视频在线 | 伊人久操 | 成 人 黄 色 视频 免费观看 | 国产免费一区二区三区最新6 | 欧美国产日韩一区 | 可以免费观看的av片 | 叶爱av在线| 麻豆综合网 | 国产一区二区电影在线观看 | 操操操日日日 | 久久蜜臀av | 日韩理论电影网 | 成人免费在线观看入口 | 日韩精品偷拍 | 日韩视频一区二区三区在线播放免费观看 | 婷婷狠狠操 | 精品视频www | 日韩精品免费在线观看 | 激情视频91| 欧美日韩另类视频 | 高清在线一区二区 | 久草视频在线资源站 | avav片 | 最新色站| 丝袜网站在线观看 | 色天天综合久久久久综合片 | 久久九九精品久久 | 久久精品99久久久久久2456 | 中文字幕在线视频免费播放 | 色狠狠狠 | 国产在线精品播放 | 国产精品久久久久久婷婷天堂 | 中文字幕超清在线免费 | 在线黄色毛片 | 亚洲h色精品 | 日本最新高清不卡中文字幕 | 午夜美女福利 | 九九热视频在线免费观看 | 国产免费久久久久 | 成人国产一区二区 | 亚洲日本成人网 | 97国产一区二区 | 人人看黄色| 国产在线免费 | 久久在线观看视频 | 夜夜操天天摸 | 亚洲毛片久久 | 国产亚洲精品久久久久久移动网络 | 成人福利av| 日本少妇高清做爰视频 | 天天玩天天干天天操 | 91av在线国产 | 黄色日视频 | 波多野结衣在线播放视频 | 中文字幕在线视频免费播放 | 久色网| 激情伊人五月天 | 日韩av进入 | 日韩av不卡播放 | 日本中文字幕在线观看 | 日韩精品在线看 | 欧美精品亚洲二区 | 四虎影视8848aamm | 日本不卡一区二区三区在线观看 | 亚洲精品在线观看不卡 | 91视频免费看网站 | 9幺看片| 亚洲欧美激情精品一区二区 | 成人毛片在线观看视频 | 精品一区二区免费 | 国产亚洲精品电影 | 亚洲日本va午夜在线影院 | 性色av免费观看 | 日本精品久久久久 | 激情五月婷婷综合网 | 草久中文字幕 | 国产香蕉视频 | 久久久精品欧美一区二区免费 | 99久久久成人国产精品 | 亚洲国内精品在线 | 日本精品视频在线播放 | 日韩国产欧美在线视频 | 国产成人av在线影院 | 国产精品福利午夜在线观看 | 国内精品久久久久久久久 | 亚洲片在线资源 | www黄色com | 少妇做爰k8经典 | 欧美日韩在线精品一区二区 | 成人免费色 | 亚洲午夜久久久久久久久电影网 | 国产成人精品亚洲 | 国产亚洲综合性久久久影院 | 欧美成年黄网站色视频 | 久久久久久美女 | 国产专区日韩专区 | 韩日电影在线免费看 | 亚洲国产精品一区二区尤物区 | 国产一区在线不卡 | 国内精品二区 | 特级a老妇做爰全过程 | 在线 日韩 av | 久久99国产精品免费网站 | 久草视频在线资源 | 色吊丝在线永久观看最新版本 | 久久精品美女视频网站 | 欧美 亚洲 另类 激情 另类 | 91精品成人久久 | 五月婷婷六月丁香 | 日韩激情免费视频 | 欧美男同视频网站 | 成年人免费电影 | 人人爱爱 | 亚洲第一av在线播放 | 国产精品1区2区3区 久久免费视频7 | 久久99热精品 | 成 人 黄 色 免费播放 | 成年人视频在线免费 | 福利一区二区在线 | 欧美性免费 | 在线播放你懂 | 91精品国产91 | 欧美亚洲久久 | 日韩理论电影在线观看 | 97在线超碰| 中文一区在线 | 精品国产美女在线 | 天天在线操 | 成人aaa毛片 | 久久视频6 | 99在线视频观看 | 日韩av二区 | 久久久久国产精品免费免费搜索 | 天天干天天摸天天操 | 色小说在线 | 久久久久久网 | 96av在线视频 | 久久综合久久久 | 国产精品欧美久久久久无广告 | 精品久久在线 | 免费看的av片| 中文字幕精品一区二区精品 | 天天色.com| 亚洲资源| 日韩欧美观看 | 欧美在线视频a | 成人av高清在线 | 日本精品一区二区在线观看 | 911香蕉| aaawww| 午夜国产在线观看 | 国产在线精品一区二区 | 在线观影网站 | 天天天天天天干 | 99r在线视频 | 一级做a视频 | 色婷婷 亚洲| 亚洲国产精品一区二区尤物区 | 国产精品免费久久久久影院仙踪林 | 97成人啪啪网 | 婷婷精品国产欧美精品亚洲人人爽 | 精品国产乱码久久久久久天美 | 丁香在线视频 | 99久久久成人国产精品 | 免费看一级黄色大全 | 天天爽综合网 | 亚洲视频在线播放 | 国产专区在线播放 | 国产精品av在线免费观看 | 99久久精品无码一区二区毛片 | 日韩精品视频久久 | 久久激五月天综合精品 | 国产1区2区3区精品美女 | a视频在线 | 国产免费亚洲 | av中文字幕第一页 | 色99视频| 久久久亚洲国产精品麻豆综合天堂 | 精品久久久久久久久久岛国gif | 国产一二三四在线视频 | 欧美激情视频一区二区三区 | 五月激情av | 一区二区高清在线 | 成人免费在线播放 | 狠狠色丁香久久婷婷综合五月 | 干av在线 | av色一区| 日韩av在线资源 | 国产精品一区二区在线 | 久久xx视频 | 日韩网站在线播放 | 国产中文字幕在线免费观看 | 亚洲四虎影院 | 视频在线99re | 久久精品精品电影网 | 美女免费视频观看网站 | 婷婷六月网 | 日日干日日色 | 日本精品一区二区三区在线观看 | 成人在线视频一区 | 成人免费观看完整版电影 | 四虎在线免费观看视频 | 毛片黄色一级 | 久久国产午夜精品理论片最新版本 | 久久国内精品 | 在线观看视频日韩 | 久久久久女人精品毛片九一 | 免费日韩 精品中文字幕视频在线 | 欧美综合在线观看 | 久久久久久蜜桃一区二区 | 天天干夜夜夜操天 | 精品国产精品国产偷麻豆 | 1000部18岁以下禁看视频 | 高清美女视频 | 成人在线一区二区三区 | 免费三级在线 | 色av婷婷| 天天操夜夜逼 | 国产精品毛片一区二区三区 | 国产a国产a国产a | 99精品免费 | 亚洲精品高清一区二区三区四区 | 黄色片网站av | 97小视频| 欧美亚洲精品一区 | 在线天堂中文在线资源网 | 久久精品久久久精品美女 | 深夜视频久久 | www.婷婷com| 国产精品2019 | 天天干,天天草 | 成人教育av | 久久久一本精品99久久精品66 | 激情婷婷 | 99人久久精品视频最新地址 | av中文资源在线 | 国产精品成人一区二区三区 | 国精产品999国精产品视频 | 国产精品99久久久久 | 成人久久久久 | 麻豆免费视频观看 | 日日日操 | 亚洲伊人婷婷 | 制服丝袜在线91 | 国产精品久久久久久久久久久免费看 | 免费瑟瑟网站 | 天天艹天天操 | 婷婷av电影| 久久国产精品二国产精品中国洋人 | 美女免费视频黄 | 日本中文在线播放 | 香蕉视频色 | 在线观看片 | 九九九热精品免费视频观看 | 香蕉久久久久久久 | 超碰人人草人人 | 久久精品人人做人人综合老师 | 欧美日韩精品二区第二页 | 日韩中文字幕免费看 | 日韩成人欧美 | 亚洲 中文 欧美 日韩vr 在线 | 日韩18p| 国产这里只有精品 | 91精品国产网站 | 日韩av黄 | 色婷婷成人网 | 日韩免费一二三区 | 日韩av一区二区在线 | 看毛片网站 | 国产精品免费av | 天堂av在线免费观看 | 91网址在线观看 | 韩国av在线 | 日韩久久片| 午夜色场| 91麻豆精品国产 | 2019中文字幕网站 | 国产一级片免费视频 | a'aaa级片在线观看 | 欧美日韩精品免费观看视频 | 国产不卡在线观看视频 | 日产av在线播放 | 狠狠做六月爱婷婷综合aⅴ 日本高清免费中文字幕 | 国产一级电影免费观看 | 亚洲成人精品在线 | 伊香蕉大综综综合久久啪 | 国产精品初高中精品久久 | 久久久久久久久精 | 欧美男同网站 | 久久久久国产精品厨房 | 欧美乱大交 | 久草在线这里只有精品 | 制服丝袜亚洲 | 久久大视频| 午夜精品久久久久久久99 | 国产日女人 | 91九色成人蝌蚪首页 | 久久国产精品99久久久久久进口 | 中文字幕有码在线播放 | 男女激情麻豆 | 日日操网站 | 中文一区在线 | 亚洲精品黄 | 欧美大片mv免费 | 免费观看一级特黄欧美大片 | 国产视频每日更新 | 在线观看日韩专区 | 视频二区 | 免费开视频 | 日韩精品一区二区三区中文字幕 | 婷婷久久一区二区三区 | 亚洲一一在线 | 精品网站999www | 一区二区三区韩国免费中文网站 | 国产美女精彩久久 | 色婷婷久久 | 综合久久精品 | 免费观看性生交 | 久久夜av| 亚洲第一区精品 | 久久久久在线 | 免费合欢视频成人app | 中文av字幕在线观看 | 99精品国产一区二区三区麻豆 | 欧美日本中文字幕 | 狠狠色婷婷丁香六月 | 成年人国产精品 | 国产精品精品久久久久久 | 97人人超| 波多野结衣动态图 | 色婷婷国产精品 | 亚洲精品国产综合99久久夜夜嗨 | 一区二区三区四区精品 | 啪啪肉肉污av国网站 | 欧美精品国产综合久久 | 天天射色综合 | www.亚洲黄| www.久久久久| 九月婷婷色 | 天天色成人 | 日韩伦理片一区二区三区 | 91av播放| 国产精品久久久久久久久久免费 | 一区二区三区韩国免费中文网站 | 91av在| 成人教育av | 在线91观看 | 中日韩男男gay无套 日韩精品一区二区三区高清免费 | 国产色黄网站 | 国产高清绿奴videos | 日本xxxx裸体xxxx17 | 久久tv | 亚洲一区二区精品 | 毛片888 | 欧美一级网站 | av大全在线看 | 欧美日韩在线精品 | 在线亚洲精品 | 九九热免费精品视频 | av一级片 | 日韩av美女 | 美女免费视频网站 | 国产福利91精品张津瑜 | 91成人国产 | 99精品热视频 | 精品九九久久 | av中文字幕免费在线观看 | 97视频总站 | 国产一级精品绿帽视频 | 国产免费一区二区三区最新6 | 日本久久久影视 | 亚洲成人免费 | 婷婷激情小说网 | 美女视频久久 | 国产在线观看a | 91福利视频免费观看 | 精品国产aⅴ麻豆 | 国产无套精品久久久久久 | 五月天激情视频在线观看 | 亚洲三级在线免费观看 | 欧美精品久久久久久久久久白贞 | 伊人久久一区 | 成人一级视频在线观看 | 蜜臀久久99精品久久久无需会员 | 国产福利资源 | 国产亚洲一区二区在线观看 | 日韩成人免费在线电影 | 亚洲精品玖玖玖av在线看 | 国产精品久久久久久久午夜片 |