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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

Spring–添加SpringMVC –第2部分

發(fā)布時(shí)間:2023/12/3 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring–添加SpringMVC –第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}訪問。 我們將在模型中添加任務(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ì)看到),我們將使用jQuery更改DOM模型并從列表中刪除分配的員工。
讓我們假裝什么都不會(huì)出錯(cuò)(我們有NO_CONTENT響應(yīng)),因此員工將總是成功地從數(shù)據(jù)庫中刪除。 因此,我們可以簡單地更改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。 讓我們?yōu)樘峁┍韱蔚奶砑涌丶?zhǔn)備控制器,該任務(wù)將從/ tasks?new訪問:

/*** 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)行。 為此,您可以將特殊方法映射到控制器,并執(zhí)行AJAX發(fā)布,例如使用帶有$ .post的 jQuery。 我認(rèn)為對(duì)于本教程來說,這太少了,但是如果您對(duì)如何在Spring中使用AJAX感興趣,請(qǐng)查看Spring 3中有關(guān)簡化Ajax的博客文章 。
在創(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ù)。

最后的代碼用于保存Task。 如前所述,在保存之前,我們將招募一些員工來執(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)存中員工的簡單輔助方法。 這并不是非常有效,我們可以通過更復(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)然要測試控制器:

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;} }

很簡單,但是@NotNull@Range注釋是什么? 好吧,我們絕對(duì)不希望用戶輸入小時(shí)數(shù)為負(fù)數(shù)或零,因此我們將使用此簡潔的JSR 303 Bean驗(yàn)證API。 要使其工作,只需將依賴于休眠驗(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 />

稍后將看到有效模型的用法。

在時(shí)間表文件夾下,我們現(xiàn)在將創(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)看到了這種方法,因此像以前一樣,我們?cè)陧?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)求,因此我們將使用非常簡單的控制器方法來處理此請(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編寫單元測試。 在測試方法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)并測試了它的邏輯。 Controller會(huì)將這些功能簡單地合并到一個(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添加單元測試:

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–添加SpringMVC –第2部分的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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

亚洲欧洲精品久久 | 国产 亚洲 欧美 在线 | 日日操狠狠干 | 日本资源中文字幕在线 | 国产黄av | 国产亚洲欧美在线视频 | 国产美女精品视频免费观看 | 日日操夜 | 808电影| av黄色一级片 | 国产专区在线播放 | 欧美视频99 | 999色视频 | 手机在线日韩视频 | 久久神马影院 | 欧美一区,二区 | 亚洲国产高清在线 | 99久久激情视频 | wwwwww国产 | 中文字幕中文字幕中文字幕 | 久久久久欠精品国产毛片国产毛生 | 黄色一级大片免费看 | 日韩视频在线一区 | 午夜精品视频福利 | 成人在线你懂得 | 国产免费观看av | 亚洲国产无 | 精品一区精品二区高清 | 性色av免费在线观看 | 麻豆视频国产在线观看 | 97国产精品一区二区 | 日韩电影一区二区三区 | 亚洲黄色一级电影 | 久久精品一二三 | 激情婷婷欧美 | 国产精品99久久久久久武松影视 | 黄污网站在线观看 | 日本在线成人 | 日韩精品在线免费播放 | 欧美日韩精品国产 | 国产手机视频在线观看 | 99中文字幕视频 | 九九九视频在线 | 午夜精品久久久久久 | 伊人影院99 | 天天干天天操天天操 | 亚洲国产日本 | 国产精品女人久久久久久 | 成人黄色电影免费观看 | 激情综合网婷婷 | 五月激情天 | 精品国产精品国产偷麻豆 | 97久久久免费福利网址 | 精品国产成人av在线免 | 日本女人在线观看 | 天天色天天干天天色 | 久久综合色播五月 | 久艹视频在线免费观看 | 久久天堂影院 | 在线观看免费成人 | 成人国产精品一区 | 国产专区视频在线观看 | www久久久久 | 日韩av电影国产 | 久久综合免费 | 六月天色婷婷 | 日韩 在线观看 | 欧美-第1页-屁屁影院 | 亚洲综合国产精品 | 国产精品一区专区欧美日韩 | 久久国产三级 | 日韩毛片久久久 | 国产97在线视频 | 伊人国产在线观看 | 欧美一级片在线 | 精品一区二区视频 | 国产资源在线播放 | 最近中文字幕 | 日韩国产精品毛片 | 97干com| 欧美激情精品久久久久久免费 | 国产一区观看 | 婷婷色狠狠| 免费看一级特黄a大片 | 亚洲区另类春色综合小说校园片 | 日本午夜在线亚洲.国产 | 亚州av免费 | 免费观看一区二区三区视频 | 国产亲近乱来精品 | 夜夜操夜夜干 | 国产精品视频app | 日韩成人高清在线 | 久久国内精品 | 超碰在线亚洲 | 一区二区三区在线看 | 一区二区三区中文字幕在线 | 亚洲欧美视频在线 | 国产精品久久久久aaaa | 成人手机在线视频 | 国产成人精品av在线观 | 日韩二区在线观看 | 综合网天天射 | 999一区二区三区 | 综合色站 | 免费在线观看亚洲视频 | 久久国产高清 | 这里有精品在线视频 | 久久视频这里只有精品 | 亚洲精品国产欧美在线观看 | 国产日产精品一区二区三区四区的观看方式 | 四虎国产免费 | 日韩精品影视 | 一区二区高清在线 | 久章草在线 | 精品国产一区二区三区久久 | 在线亚洲午夜片av大片 | 韩国在线一区二区 | 九九一级片 | 九七人人干 | 精品国产乱码久久 | 欧美亚洲国产日韩 | 色婷婷视频在线观看 | 人人玩人人添人人澡97 | 99久久精品国产一区二区三区 | 丝袜+亚洲+另类+欧美+变态 | 黄色国产区 | 日韩有码中文字幕在线 | 久久图| 精品亚洲欧美无人区乱码 | 人人澡人人干 | 精品亚洲视频在线 | 九色视频网站 | 一本一本久久a久久精品牛牛影视 | www.亚洲视频.com | 97国产精品免费 | 伊人成人激情 | 在线看国产 | 久久这里只有精品9 | av经典在线 | 欧美视频一区二 | 在线观看涩涩 | 久草在线视频精品 | 在线观av| 91麻豆精品国产自产 | 97在线观看免费观看高清 | 免费一级片在线 | 在线免费精品视频 | 欧美成人tv| 欧美孕妇视频 | 久久超碰免费 | 免费电影播放 | 日韩精品视频网站 | 81国产精品久久久久久久久久 | 国产精品久久久777 成人手机在线视频 | 精品国产一区二区三区久久久蜜臀 | 中文字幕在线影院 | 黄色成人91 | 色播五月婷婷 | 成人免费看黄 | 久久黄色片子 | 久久伊人色综合 | 成人在线一区二区三区 | 亚洲精品国产精品国自产 | 人人爽人人 | 色综合久久久久综合体桃花网 | 国产一区二区三区高清播放 | 国产成人黄色网址 | 色婷久久 | 亚洲精品日韩av | 色亚洲网| 精品99久久久久久 | 日日弄天天弄美女bbbb | 免费看片网址 | 亚洲精品视频一 | 久久综合给合久久狠狠色 | 欧美久久久久久久久久久久 | 欧美一级性视频 | 国产综合精品久久 | 日韩天堂在线观看 | 天堂av在线7| 美女免费视频观看网站 | 日本中文乱码卡一卡二新区 | 国产亚洲成av人片在线观看桃 | 国产一区国产二区在线观看 | 久久视频国产精品免费视频在线 | 日韩性片 | 黄a在线看 | 91在线成人| 久久涩涩网站 | 福利久久 | 特级毛片在线观看 | 色综合天天视频在线观看 | 午夜视频免费在线观看 | 久久久久久久久久久成人 | 日韩视频免费在线观看 | 一区二区三区国 | 美女黄频免费 | 亚洲国产精品人久久电影 | 精品国产乱码久久久久久1区2匹 | 婷婷视频| 天天操天天射天天爽 | 亚洲精品国产精品久久99 | 国产精品美女999 | 在线播放国产一区二区三区 | 亚洲成a人片在线www | 久久精品首页 | 久久综合五月天 | 日韩一级黄色av | 狠狠色丁香婷婷综合视频 | 亚洲精品久久久久久中文传媒 | 激情综合网婷婷 | 久久乐九色婷婷综合色狠狠182 | 亚洲黄色在线播放 | 黄色成人av | 成人久久影院 | 色九九在线 | 婷婷色亚洲 | 久久久久成人精品亚洲国产 | 精品一区二区日韩 | 免费观看午夜视频 | 99这里精品 | 国产精彩视频一区二区 | 一区在线观看 | 黄色亚洲片 | 91成品视频| 日韩欧美视频在线免费观看 | 97国产| 黄色小说18 | 欧美一区日韩一区 | 不卡电影免费在线播放一区 | 日韩xxxxxxxxx| 精品国产欧美一区二区三区不卡 | 久久资源在线 | 91精品国产一区二区三区 | 日本狠狠干 | a在线视频v视频 | 在线观看自拍 | 国内成人精品2018免费看 | 在线成人免费av | 久久公开视频 | 日韩理论片中文字幕 | 中文字幕av一区二区三区四区 | 91九色最新地址 | 久久久久久久久久久久久久电影 | 日韩精品久久久免费观看夜色 | 久久久久久草 | 黄色的视频 | 综合久色 | 久久免费中文视频 | 免费男女羞羞的视频网站中文字幕 | 香蕉视频国产在线 | 黄色软件在线观看视频 | 国产色网站 | 久久国产精品久久久 | 四虎成人精品 | 成年人视频在线 | 黄色小说网站在线 | 日韩18p| 一区二区伦理 | 黄色91在线| 午夜手机看片 | 西西www4444大胆在线 | 国产精品白浆 | 亚洲欧洲精品一区二区 | 国产精品综合av一区二区国产馆 | 超碰av在线播放 | 久久伊人八月婷婷综合激情 | 狠狠干综合 | 香蕉日日| 日韩精品无码一区二区三区 | 一区二区在线影院 | 午夜少妇av| 97碰在线视频 | 天堂麻豆 | 美女很黄免费网站 | 日韩欧美一区二区三区视频 | 久草观看 | 视频在线一区 | 九九久久精品视频 | av性网站 | 黄p在线播放 | 日本女人逼| 免费久久久 | 97精品国产91久久久久久 | 中文在线字幕免 | 一区二区视频免费在线观看 | 免费看三级网站 | 天天色天| 91视频免费国产 | 日本bbbb摸bbbb| 亚洲一级黄色大片 | 日本黄色免费在线观看 | 毛片网站免费在线观看 | 国产精品久久久亚洲 | 天天干,天天操 | 欧美激情精品久久久久久变态 | 国产丝袜 | 福利视频入口 | 国产精品久久久99 | 九九精品在线观看 | 在线观看视频在线观看 | 日韩精品在线视频免费观看 | 国产一级a毛片视频爆浆 | 亚洲人成免费网站 | 亚洲一区二区精品在线 | 国产精品成人品 | 国产高清av在线播放 | 深夜免费福利在线 | 麻豆久久精品 | 免费在线观看成人小视频 | 免费看片色 | 激情五月在线观看 | 成人国产精品免费 | 日韩在线中文字幕视频 | 香蕉视频4aa | 韩国av电影在线观看 | 亚洲综合欧美日韩狠狠色 | 97在线超碰 | 国产精品美乳一区二区免费 | 婷婷精品在线 | 91九色视频在线播放 | 91av电影在线观看 | 亚洲黄色免费电影 | 日日夜夜干 | 亚洲另类交 | 久久精品5| 日韩理论片在线观看 | 日韩精品中文字幕在线播放 | 91麻豆精品国产91久久久更新时间 | 国产精品视频内 | 中文日韩在线 | 久久精品九色 | 国产精品密入口果冻 | 亚洲精品女 | 九九热精品国产 | 国产糖心vlog在线观看 | 蜜臀精品久久久久久蜜臀 | 久久久久久久久爱 | 久99精品| 国产日韩精品在线观看 | 天天色天天 | 亚洲国产精久久久久久久 | 国产一级黄色片免费看 | 成+人+色综合 | 97国产大学生情侣白嫩酒店 | 午夜视频免费在线观看 | 91视频在线看 | 超碰免费成人 | 91久久国产露脸精品国产闺蜜 | 久久影院一区 | 日韩色在线 | 99中文字幕视频 | 久草视频免费看 | 色婷五月 | 草草草影院 | 久久久久国产精品一区二区 | 精品一区二区三区在线播放 | 久久99精品国产一区二区三区 | 久草青青在线观看 | 在线精品在线 | 日本中文字幕在线 | av在线播放中文字幕 | 成人黄色电影在线观看 | 欧美国产精品久久久久久免费 | 日韩大片在线免费观看 | 91精品视频网站 | 亚洲五月激情 | 国产成人三级三级三级97 | 欧美日韩首页 | 日本少妇久久久 | 91亚洲狠狠婷婷综合久久久 | 日韩婷婷| 国产在线播放一区二区三区 | 亚洲精品美女久久17c | 91在线精品一区二区 | 精品亚洲一区二区 | 中文不卡视频 | 成人在线视频一区 | 日日夜夜婷婷 | 偷拍福利视频一区二区三区 | 久久免费视频在线观看 | 蜜臀aⅴ国产精品久久久国产 | 久黄色| 黄色片毛片 | 天天操天天干天天操天天干 | 久久精品久久精品 | 成人av.com| 亚洲欧美视频在线播放 | 麻豆 free xxxx movies hd | 久草在线欧美 | 麻豆精品视频 | 一区二区视频免费在线观看 | www久久九| 亚洲精品网页 | 成人久久毛片 | 国产精品久久久久免费a∨ 欧美一级性生活片 | 久久999精品 | 亚洲精品午夜久久久久久久 | 久久夜色网| 久久福利 | 亚洲视频一 | 美女黄视频免费看 | 99热这里只有精品国产首页 | 在线观看一区二区视频 | 天天综合网入口 | 色网av | 福利网址在线观看 | 毛片美女网站 | 一区二区三区在线免费播放 | 免费久久片 | 亚洲永久精品在线观看 | 免费久久久久久 | 欧美日韩不卡一区 | 国产欧美精品xxxx另类 | 久久精品—区二区三区 | 亚洲一区动漫 | 五月婷婷丁香在线观看 | 久久久久亚洲精品国产 | 天天摸天天舔天天操 | 蜜臀aⅴ国产精品久久久国产 | 在线国产一区 | 中文字幕在线视频一区 | 美女网站视频免费黄 | 国产很黄很色的视频 | 国产成人精品一区二区三区在线观看 | 欧美日韩视频精品 | 欧美一区二区三区免费看 | 日韩手机视频 | 91视频观看免费 | 亚洲成人黄色av | 亚洲国产小视频在线观看 | 五月开心激情网 | 亚洲最新视频在线 | 亚洲 欧洲av | 日本久久99| 久久艹影院 | 狠狠躁18三区二区一区ai明星 | 毛片一区二区 | 日韩精品一卡 | 狠狠色丁香婷婷综合视频 | 久久久久久久久久免费视频 | 国产精品久久久久久久久费观看 | 91黄色小视频 | 亚洲精品动漫在线 | 亚洲伊人第一页 | 日本精品一区二区 | 午夜三级理论 | 成人免费在线网 | 久久午夜国产精品 | 久久99操| 日韩特级黄色片 | 亚洲精品美女久久久久网站 | 国产精品一区二区在线看 | 91精品视频免费在线观看 | 日本韩国精品一区二区在线观看 | 天天操天天透 | 在线免费观看涩涩 | 国产在线视频在线观看 | 91丨九色丨91啦蝌蚪老版 | 国产视频一二三 | 久久免费视频这里只有精品 | 日韩av线观看| 亚洲激情中文 | 欧美激情综合五月色丁香 | 精品1区二区| 伊人狠狠色丁香婷婷综合 | 波多野结衣一区 | 天天色 天天 | 天天操天天操天天操天天操 | 亚洲天堂网在线播放 | 五月天狠狠操 | 精品女同一区二区三区在线观看 | 午夜精品影院 | 久久精品国产免费看久久精品 | 国产一区二区不卡视频 | 国产精品一区二区三区视频免费 | 日韩精品视频在线观看免费 | 亚洲精品国产精品国自产 | 少妇性aaaaaaaaa视频 | 精品久久久久一区二区国产 | 国产视频二区三区 | 久草视频手机在线 | 黄色片网站大全 | 欧美在线一级片 | av三级在线免费观看 | 日韩免费在线观看网站 | 99久久99久久 | 日韩专区在线播放 | 色综合久久88色综合天天免费 | 亚洲欧美日韩国产一区二区三区 | 精品国产1区 | 激情小说网站亚洲综合网 | 中文字幕一区二区在线观看 | 欧美日韩在线观看视频 | 婷婷综合五月天 | 久久午夜国产精品 | 丁香六月在线观看 | 日韩精品aaa | 久久噜噜少妇网站 | 六月丁香六月婷婷 | 国产精品久久嫩一区二区免费 | 日韩精品视频免费看 | 亚洲色图22p | av电影免费在线看 | 99热999| ww视频在线观看 | 亚洲一片黄 | 成人免费视频网站在线观看 | 久久www免费人成看片高清 | 手机成人av在线 | 国产欧美在线一区二区三区 | 欧美肥妇free | 成人精品电影 | www.夜夜干.com | h视频在线看| 美女久久精品 | 日韩资源在线观看 | 伊人亚洲精品 | 国产我不卡 | 欧美一区二区三区四区夜夜大片 | 国产 成人 久久 | 超碰国产97 | 97电影在线观看 | 国产美女被啪进深处喷白浆视频 | 深爱激情婷婷网 | 成人毛片在线观看 | 久久精品日本啪啪涩涩 | 999视频在线播放 | 色综合久久中文字幕综合网 | 尤物97国产精品久久精品国产 | 国产免费av一区二区三区 | 中文字幕免费一区 | 中文字幕九九 | 五月激情电影 | 六月久久婷婷 | 九九热re| 日本中文字幕网 | 久久精品直播 | 久热久草 | 97成人资源站| 中文区中文字幕免费看 | 91在线小视频 | 国产一区二区在线播放视频 | 精品久久一区 | 久久亚洲福利视频 | 亚洲1级片| 免费av网址在线观看 | 91九色蝌蚪| 日韩美在线观看 | www色com| 狠狠干,狠狠操 | 精品国产伦一区二区三区观看体验 | 99精品网站 | 久久超级碰视频 | 免费看国产黄色 | 美女免费网站 | 国产高清视频在线 | 久久久久久久久久久久国产精品 | 亚洲理论在线观看 | 天天干夜夜爱 | 亚洲jizzjizz日本少妇 | 中文字幕国产 | 激情网综合 | 日韩精选在线 | 激情五月网站 | 国产精品久久久久久五月尺 | 中文字幕中文中文字幕 | 黄色一级在线视频 | 国产精品久久久久久久久久久久久久 | 国产精品久久久久久电影 | 麻花传媒mv免费观看 | 五月天综合激情 | 久av在线 | 久久久久免费观看 | 久草视频在线观 | 99精彩视频在线观看免费 | www免费看片com | 国产精品久久久av久久久 | 国产一级片视频 | 999久久久欧美日韩黑人 | 成人午夜电影免费在线观看 | 国产精品视频最多的网站 | 精品国产一区二区三区久久影院 | 欧美日韩一区二区在线观看 | 免费黄色av电影 | 国产亚洲精品福利 | 亚洲综合小说电影qvod | 黄色av电影在线观看 | 色婷婷a| 日韩一区二区三 | 亚洲一区二区精品在线 | 日韩在线观看三区 | 国产精品久久久久国产a级 激情综合中文娱乐网 | 日韩电影一区二区在线观看 | 天天曰天天爽 | 久久99操| 欧美aⅴ在线观看 | 综合久久久久 | 国产裸体永久免费视频网站 | 亚洲婷婷伊人 | 在线免费观看视频一区二区三区 | 五月天婷婷狠狠 | 精品欧美日韩 | 欧美日韩高清一区 | 日韩av一区二区三区在线观看 | v片在线播放 | 狠狠插狠狠操 | 99久久久久国产精品免费 | 国产69精品久久久久9999apgf | 日韩免费观看一区二区 | 国产在线美女 | 在线观看黄网站 | 天堂av网址 | 欧美精品久久久久久久 | 99色婷婷 | 天天躁天天狠天天透 | 国产在线看一区 | 播五月综合 | 色99久久 | 91成人天堂久久成人 | 欧美一级片免费播放 | 国产精品成人久久久久久久 | 六月丁香婷婷网 | 97精品国产97久久久久久免费 | 久久精品91久久久久久再现 | 国产中文字幕在线免费观看 | 久久久久成人精品亚洲国产 | 亚洲免费av在线播放 | 亚洲成人av在线播放 | 91网页版在线观看 | 免费看片成年人 | 成人毛片一区 | 91中文字幕在线 | 免费观看成人网 | 国产成人在线播放 | 黄色电影小说 | 国色天香永久免费 | 99国产精品一区二区 | 久久久久国产成人精品亚洲午夜 | 久草在线视频免费资源观看 | av一区二区三区在线 | 国产精品毛片网 | 国产a级免费 | 国产精品亚洲片在线播放 | 日本成人黄色片 | 国产精品久久久久久一区二区三区 | 欧美日韩中文在线视频 | 69xxxx欧美 | 国产成人精品在线观看 | 国产一区二区三区四区在线 | 欧美日韩精品在线免费观看 | 亚洲国产理论片 | 999成人 | 在线精品亚洲 | 99热精品在线观看 | 亚洲第一久久久 | 亚洲成人免费在线观看 | 日韩精品一区二区三区丰满 | 免费在线观看视频a | 国产精品综合久久久久久 | 国产视频欧美视频 | 亚洲精品videossex少妇 | 五月激情久久久 | 亚洲黄色免费观看 | 色噜噜狠狠狠狠色综合 | 婷婷.com| 91大神视频网站 | 最新国产精品久久精品 | 2019中文最近的2019中文在线 | 日韩免费一级a毛片在线播放一级 | 成人免费在线播放视频 | 美女视频黄免费的久久 | 五月天电影免费在线观看一区 | 日韩精品无 | 国产做爰视频 | 国产一区欧美一区 | 四虎永久精品在线 | 99超碰在线播放 | 水蜜桃亚洲一二三四在线 | 色资源在线 | 国产剧情一区在线 | 国产精品国产自产拍高清av | 亚洲黄色网络 | 国产成年人av | 在线亚洲高清视频 | 日韩免费福利 | 丁香综合 | 最新高清无码专区 | 91精彩在线视频 | 麻豆一区二区三区视频 | 国产中文自拍 | 蜜臀久久99精品久久久无需会员 | 欧美日本不卡视频 | 成人在线一区二区三区 | 午夜精品电影一区二区在线 | 国产精品成人国产乱一区 | 午夜美女av | 国产九色视频在线观看 | 日韩av片免费在线观看 | 日韩免费看视频 | 国产一级二级在线 | 午夜影院在线观看18 | 午夜精品成人一区二区三区 | 久久久久久蜜桃一区二区 | 精品视频免费观看 | 精品国产伦一区二区三区 | 国产精品黄 | 精品久久久久久综合 | 美女久久久久久久 | 有码中文在线 | 免费观看成人网 | 五月天色丁香 | 亚州黄色一级 | 四虎国产永久在线精品 | 国产美女免费观看 | 怡红院av久久久久久久 | 天天色天天色天天色 | 欧美日韩精品综合 | 国产精品自产拍 | 奇米四色影狠狠爱7777 | 天天插狠狠插 | 丁香婷婷社区 | 99精品福利| 一区二区精品在线观看 | 91网址在线| 国产精品久久一卡二卡 | 亚洲国产中文字幕在线视频综合 | 波多野结衣精品在线 | 美女视频黄频大全免费 | 手机av电影在线 | 国产一级黄色免费看 | 999久久| 精品在线观看一区二区三区 | 免费在线观看av网站 | 国产在线无 | 三级黄色a | 天天操夜夜操夜夜操 | 国产专区第一页 | 午夜三级理论 | 毛片精品免费在线观看 | 欧美日韩一级久久久久久免费看 | 国产精品乱码高清在线看 | 久久天天躁夜夜躁狠狠躁2022 | www欧美色| 国产麻豆视频网站 | 亚洲精区二区三区四区麻豆 | 九九视频这里只有精品 | 欧美日韩三区二区 | 亚洲精品综合欧美二区变态 | 精品久久久久国产免费第一页 | 在线观看免费av片 | 在线观看理论 | 日韩av一区在线观看 | 青青河边草免费直播 | 最近中文字幕大全 | 成年人国产在线观看 | 网站在线观看日韩 | 国产一级二级三级在线观看 | 久久精品综合视频 | 视频国产在线 | 国产 视频 久久 | 国产精品视频专区 | 亚洲精品毛片一级91精品 | www久久精品| 黄色小说视频在线 | 免费看的黄色小视频 | 免费日韩 精品中文字幕视频在线 | 久久99久国产精品黄毛片入口 | 日韩毛片在线一区二区毛片 | 欧洲黄色片 | 国内外成人免费在线视频 | 成人av高清在线观看 | 国内久久| 国产69精品久久久久99 | 久久精品国产精品亚洲 | 蜜桃av久久久亚洲精品 | 国产成人福利片 | 成人午夜免费福利 | 97超碰人人澡| 免费观看www7722午夜电影 | 97电影在线 | 国产免费久久精品 | 久草免费手机视频 | 91av视频网 | 有码一区二区三区 | 国产美女免费视频 | 99久久久国产精品免费99 | 婷婷av在线 | 在线 欧美 日韩 | 成年人免费观看国产 | 国产精品久久久久久久久久三级 | 国产a国产 | 91在线永久 | 久草a在线| 亚洲资源在线 | 00av视频| av中文在线影视 | 国产精品原创 | 99热高清 | 黄色av一区二区 | 丝袜美腿在线播放 | 亚洲电影自拍 | 99久久精品国产亚洲 | 丁香五月亚洲综合在线 | 日韩av影视在线观看 | 日韩免费看视频 | 欧美va天堂va视频va在线 | 一性一交视频 | 免费看黄网站在线 | av在线网站免费观看 | 在线视频日韩一区 | 激情深爱 | 日女人免费视频 | 成人性生交视频 | 日日操日日操 | 欧美日韩在线电影 | 麻豆影视在线播放 | 四虎国产永久在线精品 | 欧美网址在线观看 | 天堂av在线网 | 黄色三级免费观看 | 91精品免费看 | 欧美日韩视频一区二区 | 精品一区免费 | 免费看国产精品 | 国产一区福利 | 精品无人国产偷自产在线 | 超碰人人超碰 | 国产精品久久久一区二区 | 午夜影院日本 | 国产在线播放一区二区三区 | 国产日韩欧美自拍 | 久久久男人的天堂 | 日女人免费视频 | 日韩精品一区二区不卡 | 97超碰国产精品 | 首页国产精品 | 国产中出在线观看 | 在线网址你懂得 | 成人羞羞视频在线观看免费 | 日韩久久久 | 久草免费在线观看视频 | 日韩av在线免费播放 | 夜夜视频 | 一区二区三区四区精品 | av在线电影播放 | 波多野结衣在线观看视频 | 美女激情影院 | 99精品热 | 欧美一级欧美一级 | 国产成人精品一区二区在线 | www.综合网.com| 在线日本看片免费人成视久网 | 日韩在线观看网址 | www黄色软件 | 亚洲一区动漫 | 国产91免费观看 | 91免费看黄色 | 成人国产综合 | 欧美国产精品一区二区 | 亚洲精品字幕 | 日韩字幕在线观看 | 久久精品视频观看 | 2022中文字幕在线观看 | 激情五月播播久久久精品 | 狠狠色噜噜狠狠 | 香蕉视频国产在线 | 成人午夜片av在线看 | 中文字幕乱码视频 | av在线免费网站 | 亚洲五月| 91亚洲精品久久久中文字幕 | 在线电影 一区 | 中文字幕高清av | 国产精品一区二区三区99 | 91日本在线播放 | 亚洲成av人片一区二区梦乃 | 国产精品 中文字幕 亚洲 欧美 | 国产999在线 | 国产精品久久嫩一区二区免费 | 国内精品久久久久久久影视简单 | 国内久久 | 久射网| 国产亚洲精品电影 | 欧美精品一区二区三区一线天视频 | www.99在线观看| 不卡的av | 97精品国产一二三产区 | 久久久久久久av麻豆果冻 | 深夜福利视频在线观看 | 国产中文字幕在线播放 | 最新中文在线视频 | 亚洲国产午夜精品 | 婷婷播播网 | 香蕉视频在线网站 | 在线观看韩国av | 国产看片网站 | 日女人免费视频 | 久久国产精品一二三区 | 亚洲视频免费在线观看 | 中文在线最新版天堂 | 操操操日日日干干干 | 国产小视频在线观看免费 | www操操 | 久久久九九 | 久久久蜜桃 | 国产一级在线免费观看 | 综合亚洲视频 | 亚洲国产经典视频 | 天天色天天色 | a在线观看国产 | 亚洲黄色小说网址 | 亚洲精色 | 九热在线 | 免费视频成人 | 婷婷综合电影 | 在线精品视频免费播放 | 国产小视频你懂的在线 | 日韩在线中文字幕 | 国产一区二区久久 | 91精品国产综合久久婷婷香蕉 | 日韩精品免费一区 | 国产精品麻豆视频 | wwwww.国产| 欧美成人h版电影 | 国产午夜精品一区二区三区欧美 | 不卡中文字幕在线 | 天海冀一区二区三区 | 日本中文一级片 | 9热精品 | 91伊人久久大香线蕉蜜芽人口 | 国产精品麻豆三级一区视频 | 91精品免费在线 | 天天色播 | 黄色大片视频网站 | 亚洲毛片在线观看. | 日韩中文字幕亚洲一区二区va在线 | av短片在线 | 毛片区| 狠狠干在线播放 | 亚洲精品在线观看免费 | 黄色a视频| 六月久久婷婷 | 久久亚洲综合国产精品99麻豆的功能介绍 | 九九久久成人 | 一区二区久久久久 | 808电影免费观看三年 | 国产 在线 高清 精品 | 日韩大片免费在线观看 | 91中文字幕在线播放 | av午夜电影 | 狠狠色丁香 | 欧美视频xxx| 久久99爱视频| 最新国产在线视频 | 国偷自产中文字幕亚洲手机在线 | 黄网站污| 日韩色在线 | 欧美成人按摩 | 日韩中文字幕一区 | 国产精品系列在线 | 国产亚洲小视频 | 伊人视频 | 精品视频中文字幕 | 欧美亚洲国产精品久久高清浪潮 | 亚洲自拍偷拍色图 | 国产亚洲综合性久久久影院 | 日韩资源在线观看 | 国产成人一区二区三区在线观看 | 精品一区二区三区香蕉蜜桃 | 一区二区三区四区在线免费观看 | 日韩av在线小说 | 久久久久一区二区三区四区 | 日韩欧美一区二区不卡 | 国产自在线 | www久 | 国产伦理久久精品久久久久_ | 久久久九九 | 久久久五月天 | 天天色图| 美女免费视频观看网站 | av免费播放 | 国产精品伦一区二区三区视频 | 91精品视频一区 | 成人香蕉视频 | 深爱开心激情 | 麻豆一区在线观看 | 有码中文字幕在线观看 | 五月婷婷深开心 | 91在线精品观看 | 亚洲天堂精品视频在线观看 | 中文字幕在线观看国产 | 久草在线一免费新视频 | 免费在线观看不卡av | 国产美女视频免费观看的网站 | 草久在线| 九九久久国产精品 | 国产小视频免费在线观看 |