第二章 Spring MVC基本配置
阿新 • • 發佈:2018-12-20
本章目標
- @Controlol註解
- @RequestMaping註解
- Servlet API
- 請求引數的處理
- 返回結果的處理
為什麼使用註解
- 目前越來越多的主流框架都支援註解,同樣Spring也支援基於註解的"零配置"。
- 註解相比XML的優勢:
- 它可以充分利用 Java 的反射機制獲取類結構資訊,這些資訊可以有效減少配置的工作。
- 註解和 Java 程式碼位於一個檔案中,更加便於維護。
注意:必須在Spring 2.5版本後才能使用註解。
使用註解
註解方式將Bean的定義資訊和Bean實現類結合在一起,Spring提供的註解有
- @Component:一個普通的Bean類。
- @Repository :用於標註持久層DAO類
- @Service :用於標註業務層類
- @Controller :用於標註控制器類
示例
// 與在XML配置檔案中編寫<bean id="userDao" class="dao.impl.UserDao" /> 等效
@Repository("userDao") // Spring有預設的命名策略,使用非限定類名,第一個字母小寫,也可以在註解中使用value屬性指定元件的名稱。
public class UserDao implements IDao {
}
@Controller
-
作用:將當前類作為一個控制器類
-
如果使用註解方式,必須在Spring中新增自動掃描的路徑。
<!-- 配置Spring MVC自動掃描的路徑 -->
<context:component-scan base-package="com.znsd.controller" />
- 不適用註解,手動新增控制器的路徑
<!-- 手動新增控制器的路徑 -->
<bean class="com.lxit.controller.MyController"/>
@RequestMapping
- Spring MVC使用@RequestMapping註解為控制器指定請求的URL。
- 在控制器的類定義及方法定義處都可以標記。
- 類定義處:提供初步的對映資訊,為該類下所有請求方法新增字首。
- 方法定義處:提供進一步的對映資訊,提供方法的請求路徑。
- DispacherServlet截獲請求後,就通過控制器上的@RequestMapping提供的對映資訊確認請求所對應的處理方法。
@RequestMapping示例
- 為類新增RequestMapping更改請求路徑
@Controller
@RequestMapping("world")
public class HelloWorld {
@RequestMapping("/hello")
public String hello(){
System.out.println("hello spring mvc");
return "hello";
}
}
- 訪問請求路徑
http://localhost:8080/SpringMVCDemo/world/hello
HTTP請求
- 標準的HTTP請求 路徑。
對映請求引數,請求方法和請求頭
- @RequestMapping除了可以使用請求URL對映請求外,還可以使用請求方法,請求引數和請求頭對映請求。
- @RequestMapping的value、method、params及heads分別表示請求url,請求方法,請求引數及請求頭的對映條件,他們之間是與的關係,聯合使用多個條件可以讓請求更加精確化。
- params和headers支援簡單的表示式。
- param1:表示請求必須包含param1的請求引數。
- !param1:表示請求不能包含param1的請求引數。
- param1!=value1:表示請求引數param1不能等於value1。
- {"param1=value1","param2"}:表示請求引數param1必須等於value1,必須包含請求引數param2。
請求方法,請求引數,請求頭示例
- 設定請求地址為test1,以post方式提交,必須帶一個引數userid
@RequestMapping(value="/test1", method = RequestMethod.POST, params = "userid")
public String test1(){
System.out.println("test1");
return "hello";
}
- 設定請求地址為test2,請求頭contenttype屬性必須以text/開頭。
@RequestMapping(value = "/test2", headers = "contentType=text/*")
public String test2(){
System.out.println("test2");
return "hello";
}
使用@RequestMapping對映請求
-
@RequestMapping還支援Ant風格的URL。
-
Ant支援3中萬用字元
- ?:匹配檔案中一個字元。
- *:匹配檔案中任意字元。
- **:匹配多層路徑。
/user/*/create:匹配/user/aaa/create,/user/bbb/crate
/user/**/create:匹配/user/create,/user/aaa/bbb/create
/user/create??:匹配/user/createaa,/user/createbb
引數的處理
- @PathVariable:URL模版方式
- @RequestParam:獲取請求引數
- @RequestHeader:獲取請求頭內容
@PathVariable
- @PathVariable:用來對映URL中的佔位符。對映的變數名必須和佔位符中的名稱一致。
@RequestMapping("/delete/{userid}")
public String delete(@PathVariable("userid")int userid) {
System.out.println("userid=" + userid);
return "hello";
}
@RequestParam
- @RequtParam:獲取頁面傳遞過來的引數,GET和POST都支援。
@RequestMapping("/testRequestParam")
public String testRequestParam(@RequestParam("username")String username, @RequestParam("age")int age){
System.out.println("username=" + username);
System.out.println("age=" + age);
return "hello";
}
@RequestParam
RequestParam有三個屬性:
- value:指定引數的名稱
- required:指定引數是否為必填
- defaultValue:指定引數的預設值
當引數與傳遞的引數同名時,可以省略@Requestparam
@RequestHeader
- 請求頭包含了若干的屬性,用來獲取客戶端的資訊。通過@RequestHeader可以用來獲取請求頭中的資訊
@RequestMapping("/testRequestHeader")
public String testRequestHeader(@RequestHeader("Accept-Language") String language){
System.out.println("language=" + language);
return "hello";
}
@CookieValue
- @CookieValue:用來獲取客戶端Cookie資訊。
@RequestMapping("/testCookieValue")
public String testCookieValue(@CookieValue("JSESSIONID")String sessionid){
System.out.println("sessionid=" + sessionid);
return "hello";
}
實體物件繫結請求引數
- Spring MVC會直接將頁面元素和實體物件進行匹配,自動轉換為實體物件。並且支援級聯屬性。如address.city等。
<form action="world/user/create" method="post">
姓名:<input type="text" name="username"><br>
密碼:<input type="text" name="userpass"><br>
年齡:<input type="text" name="age"><br>
<input type="submit" value="提交" />
</form>
public class User {
private String username;
private String userpass;
private int age;
}
@RequestMapping(value = "/user/create", method = RequestMethod.POST)
public String createUser(User user) {
System.out.println(user.toString());
return "hello";
}
練習:SpringMVC實現使用者註冊
使用Spring MVC實現使用者註冊
小結
- 從頁面傳遞引數的方式
- @PathVariable
- @RequestParam
- @RequestHeader
- @CookieValue
- POJO
Servlet API
Spring MVC可以使用Servlet API作為請求方法的引數。
HttpServletRequest
HttpServletResponse
HttpSession
- Local
- InputStream
- OutputStream
- Read
- Write
示例
- 將ServletAPI作為方法的引數,方法中就可以使用api所對應的方法。
@RequestMapping("/testServletAPI")
public String testServletAPI(HttpServletRequest request,
HttpServletResponse response,HttpSession session){
return "hello";
}
org.springframework.web.servlet.mvc.annotation.ServletHandlerMethodInvoker原始碼分析
@Override
protected Object resolveStandardArgument(Class<?> parameterType, NativeWebRequest webRequest) throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
if (ServletRequest.class.isAssignableFrom(parameterType) ||
MultipartRequest.class.isAssignableFrom(parameterType)) {
Object nativeRequest = webRequest.getNativeRequest(parameterType);
if (nativeRequest == null) {
throw new IllegalStateException(
"Current request is not of type [" + parameterType.getName() + "]: " + request);
}
return nativeRequest;
}
else if (ServletResponse.class.isAssignableFrom(parameterType)) {
this.responseArgumentUsed = true;
Object nativeResponse = webRequest.getNativeResponse(parameterType);
if (nativeResponse == null) {
throw new IllegalStateException(
"Current response is not of type [" + parameterType.getName() + "]: " + response);
}
return nativeResponse;
}
else if (HttpSession.class.isAssignableFrom(parameterType)) {
return request.getSession();
}
else if (Principal.class.isAssignableFrom(parameterType)) {
return request.getUserPrincipal();
}
else if (Locale.class == parameterType) {
return RequestContextUtils.getLocale(request);
}
else if (InputStream.class.isAssignableFrom(parameterType)) {
return request.getInputStream();
}
else if (Reader.class.isAssignableFrom(parameterType)) {
return request.getReader();
}
else if (OutputStream.class.isAssignableFrom(parameterType)) {
this.responseArgumentUsed = true;
return response.getOutputStream();
}
else if (Writer.class.isAssignableFrom(parameterType)) {
this.responseArgumentUsed = true;
return response.getWriter();
}
return super.resolveStandardArgument(parameterType, webRequest);
}
處理模型資料
Spring MVC提供一下幾種途徑返回模型資料:
- ModeAndView:將處理方法的返回型別設定為ModeAndView,方法體即可通過該模型物件新增模型資料。
- Map及Model形參:當形參為Map,Model,ModelMap時,處理方法返回時,Map中的資料會自動新增到模型中。
- @SessionAttributes:將模型中的某個屬性儲存到Session中,以便多個請求之間共享這個屬性。
- @ModelAttribute:方法形參標記該註解後,形參物件就會放到模型中。
@ModelAndView
-
控制器處理方法如果返回ModelAndView,即包含檢視資訊,也包含模型資訊。
-
構造方法:提供了多種構造方法的過載
-
新增模型資料:
- ModelAndView addObject(String attributeName, Object attributeValue);
- ModelAndView addAllObjects(Map modelMap) ;
-
新增檢視
- void setView(View view);
- void setViewName(String viewName);
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){
ModelAndView modelAndView = new ModelAndView("hello");
//新增單個值
modelAndView.addObject("h","Hello Spring MVC");
return modelAndView;
}
Map形參
-
Spring MVC在內部使用了一個Model介面儲存模型資料。
-
Spring MVC在呼叫方法前會建立一個隱含的模型物件作為資料模型的儲存容器。如果傳入的引數為Map或者Model型別,SpringMVC會自動將物件儲存到模型資料中。
@RequestMapping("/testMap")
public String testMap(Map<String,Object> map){
map.put("mapdata", "map data");
return "hello";
}
@SessionAttributes
- 如果希望在多個請求之間共享某個模型的資料,則可以在控制器類上標註一個@SessionAttributes,SpringMVC會將對應的屬性儲存到Session中。
- @SessionAttributes除了可以通過屬性名指定需要放到會話中的屬性外,還可以通過模型屬性的物件型別指定哪些型別放到Session中。
- @SessionAttributes(types=User.class) :將隱含模型中所有型別為User屬性新增到Session中。
- @SessionAttributes(value={"user1","user2"}):將user1物件和user2放入Session中。
- @SessionAttributes(types={User.class,Dept.class})
- @SessionAttributes(value={"user1","user2"},types={Dept.class}):
- @SessionAttributes只能用來修飾類。
@SessionAttributes
- 儲存使用者到Session中
@SessionAttributes("user")
@Controller
@RequestMapping("world")
public class HelloWorld {
@RequestMapping("/testSession")
public String testSession(Map<String,Object> map){
User user = new User();
user.setUsername("zhangsan");
user.setUserpass("123");
user.setAge(20);
map.put("user", user);
return "hello";
}
}
@SessionAttributes註解只能放在類的前面,而不能放在方法前。
@ModelAttribute
- 在方法定義上定義@ModelAttribute註解:
- SpringMVC呼叫方法前,會逐個呼叫方法上標註了@ModelAttributes的方法。
- 將@ModelAttributes中的屬性儲存到map中,可以在執行表單提交生成物件之前,替換執行方法同名的形參。
- 在方法的形參上定義@ModelAttribute註解:
- 可以從隱含物件中獲取隱含的模型中獲取物件,再將請求引數繫結到物件中,再傳入形參。
- 將方法形參物件新增到模型中。
- 執行流程:
- 執行@ModelAttribute註解修飾的方法:從資料庫中取出物件,把物件放入到Map中,鍵為:user
- Spring MVC 從Map中取出User物件,並把表單的請求引數賦給該User物件對應的屬性。
- Spring MVC 把上述物件傳入目標方法的引數。引數名稱必須和@ModelAttribute 的Map中儲存的user物件同名。
@ModelAttribute 例子
@ModelAttribute
public User getUser(){
User user = new User();
System.out.println("呼叫 getUser 方法");
//預設儲存名字為類名首字母小寫的user物件到Request中
return user;
}
@ModelAttribute
public void getUserById(Integer id,Map<String,Object> map){
User myuser = new User();
map.put("myuser", myuser);
//手動指定user物件的名稱,到Request中
System.out.println("呼叫 getUser 方法");
}
@RequestMapping("/testModelAttribute")
public String testModelAttribute(@ModelAttribute("user")User user){
System.out.println(user);
return "hello";
}
由@SessionAttributes引發的異常
- 當類使用@SessionAttributes修飾,而方法中使用了和SessionAttributes修飾同名的對映引數,確沒有新增@ModelAttribute修飾時,則會報錯。
解決方案
- 引數前使用@ModelAttribute修改對映的名稱。
- 和@SessionAttributes中的名稱不同。
總結
- @Controlol註解
- @RequestMaping註解
- Servlet API
- 請求引數的處理
- 返回結果的處理