springmvc學習筆記
學習springmvc框架原理
包括哪些組件:
- 前端控制器(中心)
- 處理器映射器(三大組件)
- 處理器適配器(三大組件)
- 視圖解析器(三大組件)
-
springmvc框架原理
1.用戶發起請求,請求到DispatcherServlet前端控制器DispatcherServlet(中央調度),負責request和response,負責調用處理器映射器查找Handler,負責調用處理器適配器執行Handler,有了前端控制器降低了各各組件之間的耦合性,系統擴展性提高。
2.DispatcherServlet前端控制器請求處理器映射器HandlerMapping查找Handler根據用戶請求的url,根據一定的規則去查找(比如:xml配置,注解)
3.HandlerMapping處理器映射器將Handler返回給前端控制器
4.DispatcherServlet前端控制器調用HandlerAdapter處理器適配器執行Handler程序員編寫的Handler是按照適配器要求的規則去執行Handler
5.HandlerAdapter處理器適配器執行Handler
適配器按照一定規則去執行Handler
6.Handler執行完成,返回ModelAndView
ModelAndView是springmvc的封裝對象,將model和view封裝在一起。
7.HandlerAdapter處理器適配器將ModelAndView返回給前端 控制器
8.前端控制器調用視圖解析器,進行視圖解析,解析完成給前端控制器返回View
View是springmvc的封裝對象,是一個接口,實現類包括jspview,pdfview。。。。
9.前端控制器調用view進行視圖渲染
將模型數據填充到view(將model數據填充到request)響應給用戶
10、前端控制器響應給用戶。
創建一個springmvc
在web.xml中配置servlet。
指定 contextConfigLocation讓前端控制器找到springmvc的全局配置文件。
<!-- 前端控制器 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- contextConfigLocation指定 springmvc的全局配置文件 如果 contextConfigLocation不指定,默認找配置文件名稱:servlet的name+"-servlet.xml" -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
在springmvc.xml中配置:
處理器映射器:
<!-- 處理器映射器 根據url匹配bean的name 處理器映射器實現了HandlerMapping接口 -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
處理器適配器:
<!-- 處理器適配器 實現了HandlerAdapter接口 action按照適配器要求開發 ,規則是實現Controller接口 -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
視圖解析器:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 視圖的前綴 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 視圖的后綴 -->
<property name="suffix" value=".jsp" />
</bean>
編寫Handler
由于使用SimpleControllerHandlerAdapter適配器,要求編寫的Handler實現Controller接口。
需求:向頁面顯示一行信息“helloworld”
public class Hello implements Controller{
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("message", "HelloWorld");
modelAndView.setViewName("hello");
return modelAndView;
}
}
編寫jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${message}
</body>
</html>
配置Handler
由于使用處理器映射器BeanNameUrlHandlerMapping,要求在spring的容器配置Handler,Handler的name就是url。
在springmvc.xml中配置:
<!--配置 action-->
<bean name="/helloworld.action" class="com.luoluo.springmvc.Hello" />
強化三大組件
處理器映射器:
作用:根據url找到Handler.
BeanNameUrlHandlerMapping:根據url匹配bean的name 處理器映射器實現了HandlerMapping接口
SimpleUrlHandlerMapping:將url進行集中配置。
<!-- 簡單url映射 -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/hello1.action">hello_controller</prop>
<prop key="/hello2.action">hello_controller</prop>
<prop key="/hello3.action">hello_controller3</prop>
</props>
</property>
</bean>
hello_controller是action這個bean的id。
處理器適配器:
作用:按照一定規則去執行Handler
SimpleControllerHandlerAdapter:規則是Handler要實現Controller接口
HttpRequestHandlerAdapter:規則是Handler要實現HttpRequestHandler接口。
注意:多個適配器可以共存!
使用注解開發
配置注解處理器映射器和適配器:
<!-- 組件掃描 只掃描action -->
<context:component-scan base-package="com.luoluo.springmvc" />
<!-- 使用<mvc:annotation-driven />替換上邊定義的處理器映射器和適配器 -->
<mvc:annotation-driven />
<!--注解映射器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping" />
<!--注解適配器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter" />
編寫Handler:
package com.luoluo.springmvc;
import com.luoluo.domain.Student;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* Created by luozhiyun on 17/2/11.
*/
@Controller
public class StudentAction {
@RequestMapping("/editstudent")
public String editstudent(Model model) {
Student student = new Student();
student.setId("s001");
student.setName("張三");
student.setAge(32);
student.setBirthday(new Date());
model.addAttribute("student", student);
return "student/editstudent";
}
@RequestMapping("/editstudentsubmit")
public String editstudentsubmit(Student student) {
System.out.println(student);
return "success";
}
//這里需要進行日期轉換,一般的情況是放在父類里面,以提高代碼的復用性
@InitBinder
public void initBinder(HttpServletRequest request,
ServletRequestDataBinder binder) throws Exception {
//將student類中的birthday屬性進行日期轉換,類型和birthday一致
binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));
}
}
jsp頁面:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>學生信息修改</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/editstudentsubmit.action" method="post">
<input name="id" value="${student.id}" type="hidden">
姓名:<input name="name" value="${student.name}"/>
<br/>
年齡:<input name="age" value="${student.age}"/>
<br/>
出生日期:<input name="birthday" value="<fmt:formatDate value="${student.birthday}" pattern="yyyy-MM-dd"/>"/>
<br/>
<input type="submit" value="提交 "/>
</form>
</body>
</html>
與struts2的不同
1.springmvc的入口是servlet,struts是filter
2.springmvc是基于方法開發,struts是基于類開發action類中,一個url對應一個方法,對Handler對象,springmvc框架將一個方法封裝到Handler中(只有一個method)。Struts對action類進行實例化。
Springmvc中的action可以使用單例也可以使用多例,建議使用單例(對相同的方法只new一個)。Struts不能用單例,因為struts傳遞參數是通過成員變量。
Springmvc通過形參接收參數好處:更符合軟件開發的思想,和service接口類似。
3.Struts采用值棧存儲請求和響應的數據,通過OGNL存取數據, springmvc通過參數解析器是將request請求內容解析,并給方法形參賦值,將數據和視圖封裝成ModelAndView對象,最后又將ModelAndView中的模型數據通過reques域傳輸到頁面。Jsp視圖解析器默認使用jstl。
常用注解
@controller
標識該類為控制器類,@controller、@service、@repository分別對應了web應用三層架構的組件即控制器、服務接口、數據訪問接口
@RequestMapping
進行url映射,一個方法對應一個url,定義方法:在action方法上定義requestMapping
例如:定義在類上和方法上所代表的路徑就是根路徑+子路徑:/stu/querystudent.action
@Controller
@RequestMapping("/stu")
public class StudentAction {
@RequestMapping("/querystudent")
public String querystudent(Model model) {
return "student/querystudent";
}
URI模版模式映射
定義在方法上:用{},然后通過方法進行傳參,
在RequestMapping上設置{},然后在方法上設置注解@PathVariable進行傳參
@RequestMapping(value = "/editstudent/{id}",method = RequestMethod.GET)
public String editstudent(Model model, @PathVariable String id) {
System.out.println("id="+id);
Student student = new Student();
student.setId("s001");
student.setName("張三");
student.setAge(32);
student.setBirthday(new Date());
model.addAttribute("student", student);
return "student/editstudent";
}
請求方法限定
在RequestMapping設定method參數來限制請求方式
@RequestMapping(value = "/editstudent/{id}",method = RequestMethod.GET)
可以限定為get、post、或兩者都可以
限定GET方法
@RequestMapping(method = RequestMethod.GET)
@RequestMapping(method = RequestMethod.POST)
@RequestMapping(method={RequestMethod.GET,RequestMethod.POST})
@RequestParam綁定單個請求參數
value:參數名字,即入參的請求參數名字,如value=“studentid”表示請求的參數區中的名字為studentid的參數的值將傳入;
required:是否必須,默認是true,表示請求中一定要有相應的參數,否則將報400錯誤碼;
defaultValue:默認值,表示如果請求中沒有同名參數時的默認值
需求 :
學生查詢方法中必須要有一個參數group(學生分組)
@RequestMapping("/querystudent")
public String querystudent(
Model model,
@RequestParam(value = "groupid", required = true, defaultValue = "g001") String group,
UserVo userVo) throws Exception {
//System.out.println("group="+group);
//將學生信息列表在頁面上展示
List<Student> list = new ArrayList<Student>();
Student student = new Student();
student.setId("s001");
student.setName("張三");
student.setAge(32);
student.setBirthday(new Date());
Student student2 = new Student();
student2.setId("s002");
student2.setName("李四");
student2.setAge(32);
student2.setBirthday(new Date());
list.add(student);
list.add(student2);
model.addAttribute("list", list);
return "student/querystudent";
}
表單對象傳值
使用domain屬性名傳遞
jsp上的name屬性定義
<form method="post" action="${pageContext.request.contextPath}/loginsubmit.action">
用戶名:<input type="text" name="name" /><br/>
password:<input type="text" name="password" /><br/>
<input type="submit" >
</form>
Action方法的定義:
@RequestMapping("/loginsubmit")
public String loginsubmit(User user){
return "";
}
使用domain屬性名傳遞
jsp上的name屬性定義:
<form action="${pageContext.request.contextPath }/stu/editstudentsubmit.action" method="post">
<input name="id" value="${student.id}" type="hidden">
姓名:<input name="student.name" value="${student.name}"/>
<br/>
年齡:<input name="student.age" value="${student.age}"/>
<br/>
出生日期:<input name="student.birthday" value="<fmt:formatDate value="${student.birthday}" pattern="yyyy-MM-dd"/>"/>
<br/>
<input type="submit" value="提交 "/>
</form>
Action方法的定義:
在形參不能直接使用domain接受上面的參數,應該使用包裝對象接受上面的參數
public class StudentVo {
private Student student;
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
}
Action形參的定義:
@RequestMapping("/editstudentsubmit")
public String editstudentsubmit(Student student, StudentVo studentVo) {
System.out.println(studentVo);
System.out.println(student);
return "success";
}
結果轉發
Redirect:請求重定向
? 瀏覽器中地址欄的url通過Redirect變了,重新定義了一個request。
Action方法通過Redirect重定向到另一個方法,方法形參無法帶到另一個方法。
定義方法:
return "redirect:querystudent.action";
Forward:頁面轉發
? 瀏覽器中地址欄的url通過Forward不變,沒有重新定義了一個request。
Action方法通過Forward轉發到另一個方法,方法形參可以帶到另一個方法。
定義方法:
return "forward:querystudent.action";
@RequestBody @ResponseBody實現json數據交互
需求一:請求json響應json
頁面傳入json格式的數據(json串)
-
Action方法形參:
使用java對象接收json串。使用@RequestBody
-
Action返回一個json串,將java對象轉json
使用@ResponseBody
@RequestMapping("/j2jAction")
public @ResponseBody
Student j2jAction(@RequestBody Student student) {
System.out.println(student);
return student;
}
頁面:
function responseJson() {
$.ajax({
method:"POST",
url:"${pageContext.request.contextPath}/j2jAction.action",
contentType:"application/json;charset=utf-8",
data:'{"name":"luoluo","age":13}',
success:function (data) {
alert(data.name);
}
});
}
需求二:請求key/value,響應json
- 頁面傳入普通表單數據:key/value
- Action方法接收到key/value數據,返回一個json串
@RequestMapping("/v2jAction")
public
@ResponseBody
Student v2jAction(Student student) {
System.out.println(student);
return student;
}
攔截器
配置
針對某一個handlerMapping配置攔截器
<bean
class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="handlerInterceptor1"/>
<ref bean="handlerInterceptor2"/>
</list>
</property>
</bean>
<bean id="handlerInterceptor1" class="springmvc.intercapter.HandlerInterceptor1"/>
<bean id="handlerInterceptor2" class="springmvc.intercapter.HandlerInterceptor2"/>
間接配置全局攔截器:讓springmvc框架自動向每個handlerMapping中注冊攔截器
<!--攔截器 -->
<mvc:interceptors>
<!--多個攔截器,順序執行 -->
<mvc:interceptor>
<mvc:mapping path="/**"/>//攔截所有請求
<bean class="cn.itcast.springmvc.interceptor.HandlerInterceptor1"></bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="cn.itcast.springmvc.interceptor.HandlerInterceptor2"></bean>
</mvc:interceptor>
</mvc:interceptors>
定義攔截器:
package cn.itcast.springmvc.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
*
* <p>Title: HandlerInterceptor1</p>
* <p>Description: 攔截器1</p>
* <p>Company: www.itcast.com</p>
* @author 傳智.燕青
* @date 2014-12-15下午5:57:23
* @version 1.0
*/
public class HandlerInterceptor1 implements HandlerInterceptor {
//handler,springmvc根據url找到Handler(只有一個方法)
//執行時機:進入Handler方法之前執行,如果返回false表示攔截,如果返回true表示放行
//使用場景:用于用戶身份校驗,用戶權限攔截校驗
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("HandlerInterceptor1..preHandle");
return false;
}
//執行時機:進入Handler方法之后 ,在返回modelAndView之前
//使用場景:使用modelAndView,向頁面傳遞通用數據,使用統一的view
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("HandlerInterceptor1..postHandle");
}
//執行時機:Handler方法執行完成,(modelAndView已經返回)
//使用場景:統一異常處理,統一記錄系統日志,用于action方法執行監控(在preHandle記錄一個時間點,在afterCompletion記錄執行結束時間點,將結束時間點減去開始執行時間點,得到執行時長)
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("HandlerInterceptor1..afterCompletion");
}
}
測試
-
兩個攔截器都放行
HandlerInterceptor1..preHandle
HandlerInterceptor2..preHandle
HandlerInterceptor2..postHandle
HandlerInterceptor1..postHandle
HandlerInterceptor2..afterCompletion
HandlerInterceptor1..afterCompletion
結論:
preHandle是按照攔截器定義順序執行,
postHandle和afterCompletion是按照攔截器定義逆向執行。
-
第一個放行第二個不放
HandlerInterceptor1..preHandle
HandlerInterceptor2..preHandle
HandlerInterceptor1..afterCompletion
結論:
只要有一個攔截器不放行,action方法無法完成。
如果攔截器放行,afterCompletion才會執行。
只要有一個攔截器不放行,postHandle不執行。
-
兩個都不放行
HandlerInterceptor1..preHandle
結論:
只要有一個攔截器不放行,action方法無法完成。
只要有一個攔截器不放行,postHandle不執行。
<mvc:annotation-driven />
<!-- 使用<mvc:annotation-driven />替換上邊定義的處理器映射器和適配器 -->
<mvc:annotation-driven />