1. 視圖和視圖解析器
- 視圖解析器
- 請求處理方法執行完成后,最終返回一個ModelAndView對象,對於返回String,View 或 ModelMap
等類型的處理方法, SpringMVC 也會在內部將它們裝配成一個ModelAndView對象; - SpringMVC借助視圖解析器(ViewResolver)得到最終的視圖對象(View),最終的視圖可以是 JSP,
EXCEL,PDF等各種表現形式的視圖;
- 請求處理方法執行完成后,最終返回一個ModelAndView對象,對於返回String,View 或 ModelMap
- 視圖
- 視圖的作用是渲染模型數據,將模型里的數據以某種形式呈現給客戶;
- View 接口,位於
org.springframework.web.servlet
包中; - 視圖對象由視圖解析器負責實例化.由於視圖是無狀態的,所以它們不會有線程安全的問題;
2. mvc:view-controller
標簽
- 作用: 在不需要Controller處理request請求的情況下,直接將設置的View交給相應的視圖解析器解析為
視圖;
// 在 springDispatcherServlet-servlet.xml 中配置
<mvc:view-controller path="/自定義網址" view-name="View視圖頁面名稱"/>
// 還需要使用 mvc:annotation-driven 標簽,否則,在使用 Controller 處理request請求時,訪問
// 該頁面,會報異常;
<mvc:annotation-driven></mvc:annotation-driven>
3. 自定義視圖
3.1 具體實現步驟
- 建立專門的view包:
cn.itcast.springmvc.views
; - 編寫一個View接口的實現類;
- 將
BeanNameViewResolver
配置進springmvc配置文件;
// 1. 創建View接口的實現類
@Component
public class HelloView implements View{
public String getContentType(){
return "text/html;charset=UTF-8";
}
public void render(Map<String,?> model, HttpServletRequest request,
HttpServletResponse response) throws Exception{
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("自定義視圖顯示");
response.getWriter().flush();
response.getWriter().close();
}
}
// 2. springDispatcherServlet-servlet.xml 中配置視圖解析器
<bean id="BeanNameViewResolver"
class="org.springframework.web.servlet.view.BeanNameViewResolver">
<!-- 自定義order,越小越靠前 --
<property name="order" value="30"></property>
</bean>
// 3. demo.java
@Controller
public class HelloWorld{
@RequestMapping(value="/helloworld",method=RequestMethod.GET)
public String helloworld(){
System.out.println("執行成功");
return "helloView";
}
}
4. 請求轉發和重定向
- 地址欄是否變化,參數能否取得,發送了幾次請求;
- 請求轉發:一個請求一個響應;
- 重定向:兩個請求兩個響應,兩個請求之間毫無關系,所以第一個請求里面保存的信息在第二個請求里面無法獲得;
// demo.java
@Controller
public class Helloworld{
// 重定向
@RequestMapping(value="/redirect",method=RequestMethod.GET)
public String helloworld(){
System.out.println("程序運行正常");
return "redirect:/1.jsp";
}
// 轉發
@RequestMapping(value="/forward",method=RequestMethod.GET)
public String helloworld(){
System.out.println("程序運行正常");
return "forward:/2.jsp";
}
}
5. RESTful SpringMVC CRUD
5.1 REST(Representational State Transfer) 架構的主要原則
- 網絡上的所有事物都可被抽象為資源(Resource);
- 每個資源都有一個唯一的資源標識符(Resource Identifier);
- 同一資源具有多種表現形式(xml,json等);
- 對資源的各種操作不會改變資源標識符;
- 所有的操作都是無狀態的(Stateless);
- 符合REST原則的架構方式,即可稱為RESTful;
// 需求:
// 1. 查詢
// * URI: emps
// * 請求方式: GET
// 2. 添加所有員工信息
// 2.1 顯示添加頁面:
// * URI: emp
// * 請求方式: GET
// 2.2 添加員工
// * URI: emp
// * 請求方式: POST
// * 添加完成后,重定向到 list 頁面
// 3. 刪除
// * URI: emp/{id}
// * 請求方式: DELETE
// 4. 修改操作 (其中 lastName 不可修改!!)
// 4.1 顯示修改頁面
// * URI: emp/{id}
// * 請求方式: GET
// 4.2 修改員工信息
// * URI: emp
// * 請求方式: PUT
// * 完成修改,重定向到 list 頁面
// index.jsp
顯示所有員工信息: <a href="${pageContext.request.contextPath}/emps">顯示所有</a>
// list.jsp (顯示所有員工信息頁面)
<head>
<!-- 引入 jquery 腳本 -->
<script type="text/javascript"
src="${pageContext.request.contextPath}/scripts/jquery-3.2.1.min.js">
</script>
<!-- 使用表單提交 delete 請求 -->
<script type="text/javascript">
$(function(){
$(".deleteCss").click(function(){
var action = $(this).attr("href");
$("#deleteForm").attr("action",action).submit();
<!-- 取消 click 的默認事件 -->
return false;
});
});
</script>
</head>
<body>
<h2>顯示所有員工</h2>
<c:if test="${empty requestScope.employees}">
it's nothing...
</c:if>
<c:if test="${not empty requestScope.employees}">
<table border="1" cellpadding="10" cellspacing="2">
<tr>
<td>id</td>
<td>lastName</id>
<td>email</td>
<td>gender</id>
<td>department</td>
<td>edit</id>
<td>delete</td>
</tr>
<c:forEach items="${requestScope.employees}" var="employee">
<tr>
<td>${employee.id}</td>
<td>${employee.lastName}</id>
<td>${employee.email}</td>
<td>${employee.gender == 1 ? 'male' : 'female'}</id>
<td>${employee.department.departmentName}</td>
<td>
<a href="${pageContext.request.contextPath}/emp/${employee.id}">
edit</a></id>
<td>
<a class="deleteCss"
href="${pageContext.request.contextPath}/emp/${employee.id}>
delete
</a>
</td>
</tr>
</c:forEach>
</c:if>
添加員工: <a href="${pageContext.request.contextPath}/emp">添加</a>
<br/>
<!-- 需要執行RESTful風格的刪除,需要使用form表單 post 提交 -->
<form id="deleteForm" action="" method="post">
<input type="hidden" name="_method" value="DELETE"/>
</form>
</body>
// add.jsp (添加之前的編輯頁面)
// 需要引入 springframework 的標簽
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<body>
<h2>添加客戶</h2>
<form:form action="${pageContext.request.contextPath}/emp" method="post"
modelAttribute="employee">
lastName:<form:input path="lastName"/><br/>
email:<form:input path="email"/><br/>
gender:<form:radiobuttons path="gender" items="${requestScope.genders}"/><br/>
department:<form:select path="department.id" items="${requestScope.departments}"
itemLabel="departmentName" itemValue="id"/></form:select><br/>
<input type="submit" value="保存"/>
</form:form>
</body>
// edit.jsp (修改頁面)
<h2>修改客戶</h2>
<form:form action="${pageContext.request.contextPath}/emp" method="post"
modelAttribute="employee">
<input type="hidden" name="_method" value="PUT"/><br/>
<form:hidden path="id"/>
email:<form:input path="email"/><br/>
gender:<form:radiobuttons path="gender" items="${requestScope.genders}"/><br/>
department:<form:select path="department.id" items="${requestScope.departments}"
itemLabel="departmentName" itemValue="id"/></form:select><br/>
<input type="submit" value="修改"/>
</form:form>
</body>
// springDispatcherServlet-servlet.xml 配置文件
// 若將 web.xml 中 的 DispatcherServlet 請求映射配置為 "/", 則 SpringMVC 將捕獲 Web 容器
// 的所有請求,包括靜態資源的請求, SpringMVC 會將它們當成一個普通請求處理,會找不到對應處理器;
// 可以在 SpringMVC 的配置文件中配置 <mvc:default-servlet-handler/>,以解決靜態資源的問題:
// * <mvc:default-servlet-handler/> 將在 SpringMVC 上下文中定義一個
// * DefaultServletHttpRequestHandler, 它會對 DispatcherServlet 的請求進行篩查,
// * 如果發現是沒有經過映射的請求,就將該請求交由WEB應用服務器默認的 Servlet 處理,如果不是
// * 靜態資源的請求,才由 DispatcherServlet 繼續處理;
// 一般 WEB 應用服務器默認的 Servlet 的名稱都是 default, 若所使用的 WEB 服務器的默認
// Servlet 名稱不是 default, 則需要通過 default-servlet-name 屬性顯示指定;
<!-- 處理靜態資源導入, 例如導入 jquery -->
<mvc:default-servlet-handler/>
<!-- 如果只有 mvc:default-servlet-handler, 注解類失效, 還需要配置 annotation-driven -->
<mvc:annotation-driven></mvc:annotation-driven>
// web.xml
// 默認情況下,PUT和DELETE請求是無法提交表單數據的;
// 解決方案: 在web.xml中配置spring提供的過濾器解決或者在頁面中使用form表單設置
<filter>
<filter-name>HttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--
將POST請求轉化為DELETE或者是PUT
要用_method指定真正的請求參數
-->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
// EmployeeHandler.java
@Controller
public class EmployeeHander{
@Autowired
private EmployeeService employeeService;
@Autowired
private DepartmentService departmentService;
// 跳轉進入添加員工信息頁面
// 需要查詢出性別,全部部門
@RequestMapping(value="/emp",method=RequestMethod.GET)
public String addPre(Map<String,Object> map){
// 1. 查詢出全部部門
map.put("departments",departmentService.getDepartments());
// 2. 查出性別
map.put("genders",getGenderUtils());
// 3. 新建承載的 bean, 實現與前台 form 表單的對應
// 在進入 spring form 標簽設定 binding 對象頁面前,必須有一個 binding 的對象
// 放在 context 中,spring 才能 binding.類似struts的context statck和value stack.
// 可以在 form 表單上通過 modelAttribute 屬性指定綁定的模型屬性,
// 如果沒有指定該屬性, 則默認從 request 域對象中讀取 command 的表單 bean.
// 如果bean不存在,則會引發異常.
map.put("employee", new Employee());
return "add";
}
// 保存員工信息
@RequestMapping(value="/emp",method=RequestMethod.POST)
public String add(Employee employee){
employeeService.add(employee);
return "redirect:/emps";
}
// 刪除員工信息
@RequestMapping(value="/emp/{id}",method=RequestMethod.DELETE)
public String delete(@PathVariable("id") Integer id){
employeeService.delete(id);
return "redirect:/emps";
}
// 修改之前,跳轉到修改頁面
// lastName 不可修改
@RequestMapping(value="/emp/{id}",method=RequestMethod.GET)
public String editPre(@PathVariable("id") Integer id, Map<String,Object> map){
map.put("departments",departmentService.getDepartments());
map.put("genders",getGenderUtils());
map.put(employee,employeeService.get(id));
return "edit";
}
// 修改
@RequestMapping(value="/emp",method=RequestMethod.PUT)
public String update(Employee employee){
employeeService.save(employee);
return "redirect:/emps";
}
// 保證 lastName 不能修改,且值不變
@ModelAttribute
public void getEmployeeById(@RequestParam(value="id",required=false) Integer id,
Map<String,Object> map){
if(null != id){
map.put("employee",employeeService.get(id));
}
}
// 查詢所有員工
@RequestMapping(value="/emps",method=RequestMethod.GET)
public String list(Map<String,Object> map){
map.put("employees",employeeService.findAll());
return "list";
}
// 模擬從 LOV(list of value) 數據庫查詢出 gender 的值
private Map<String, String> getGenderUtils(){
Map<String,String> genders = new HashMap<String,String>();
genders.put("1","male");
genders.put("0","female");
return genders;
}
}
參考資料