1. 程式人生 > 實用技巧 >SpringMVC 學談 (第二章)

SpringMVC 學談 (第二章)

寫在前面:分享技術,共同進步,有不足請見諒,相關意見可評論告知 ~

程式設計路漫之遠兮,運架構式之簡化
勸君專注案前事,亦是杯酒敬蒼生;

目錄

控制器Controller

概述:控制器負責提供訪問應用程式的行為,通常通過介面定義註解定義兩種方法實現。

作用:控制器負責解析使用者的請求並將其轉換為一個模型。在Spring MVC中一個控制器類可以包含多個方法,對於Controller的配置方式有很多種

實現Controller介面

Controller是一個介面,在org.springframework.web.servlet.mvc包下,介面中只有以下一個方法;

//實現該介面的類獲得控制器功能
public interface Controller {
   //處理請求且返回一個模型與檢視物件
   ModelAndView handleRequest(HttpServletRequest var1, HttpServletResponse var2) throws Exception;
}

測試

//定義控制器
//注意點:不要導錯包,實現Controller介面,重寫方法;
public class ControllerTest1 implements Controller {

   public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
       //返回一個模型檢視物件
       ModelAndView mv = new ModelAndView();
       mv.addObject("msg","Test1Controller");
       mv.setViewName("test");
       return mv;
  }
}

編寫完畢後,去Spring配置檔案中註冊請求的beanname對應請求路徑,class對應處理請求的類

<bean name="/t1" class="com.kuang.controller.ControllerTest1"/>
編寫前端test.jsp,注意在WEB-INF/jsp目錄下編寫,對應我們的檢視解析器

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
   <title>lzh</title>
</head>
<body>
${msg}
</body>
</html>

注意:實現介面Controller定義控制器是較老的辦法

缺點:一個控制器中只有一個方法,如果要多個方法則需要定義多個Controller;定義的方式比較麻煩;

Tips:
@Component 元件
@Service service
@Controller controller
@Repository dao

MVVM: M V VM(ViewModel雙向繫結)

類上標註@Controller 且方法上沒有標註@ResponseBody,return的值會被檢視解析器處理-----> 被此註解標註的類,所有方法,如果返回值式String ,並且有具體的夜面可以跳轉,那麼就會被檢視解析器解析

使用註解編寫(重要)

@Controller操作

可見Spring學記 (第二章)
跳轉

@Controller註解型別用於宣告Spring類的例項是一個控制器(IOC還有另外3個註解);

1、Spring可以使用掃描機制來找到應用程式中所有基於註解的控制器類,為了保證Spring能找到控制器,需要在配置檔案中宣告元件掃描。

<!-- 自動掃描指定的包,下面所有註解類交給IOC容器管理 -->
<context:component-scan base-package="com.kuang.controller"/>

2、增加一個ControllerTest2類,使用註解實現;

//@Controller註解的類會自動新增到Spring上下文中
@Controller
public class ControllerTest2{

   //對映訪問路徑
   @RequestMapping("/t2")
   public String index(Model model){
       //Spring MVC會自動例項化一個Model物件用於向檢視中傳值
       model.addAttribute("msg", "ControllerTest2");
       //返回檢視位置
       return "test";
  }

}

3、執行tomcat測試

總結:我們的兩個請求都可以指向一個檢視,但是頁面結果的結果是不一樣的,從這裡可以看出檢視是被複用的,而控制器與檢視之間是弱偶合關係


@RequestMapping操作

@RequestMapping註解用於對映url到控制器類或一個特定的處理程式方法。可用於類或方法上。用於類上,表示類中的所有響應請求的方法都是以該地址作為父路徑。

只註解在方法上面情況

@Controller
public class TestController {
   @RequestMapping("/h1")
   public String test(){
       return "test";
  }
}

訪問路徑:http://localhost:8080 / 專案名 / h1

專案名 ---> Application.context
同時註解類與方法

@Controller
@RequestMapping("/admin")
public class TestController {
   @RequestMapping("/h1")
   public String test(){
       return "test";
  }
}

訪問路徑:http://localhost:8080 / 專案名/ admin /h1 , 需要先指定類的路徑再指定方法的路徑;

RestFul 風格

概念:Restful就是一個資源定位及資源操作的風格。基於這個風格設計的軟體可以更簡潔,更有層次,更易於實現快取等機制。

資源操作:使用POST、DELETE、PUT、GET,使用不同方法對資源進行操作。

傳統方式

操作資源 :通過不同的引數(post 和 get)來實現不同的效果

http://127.0.0.1/item/queryItem.action?id=1 查詢,GET

http://127.0.0.1/item/saveItem.action 新增,POST

http://127.0.0.1/item/updateItem.action 更新,POST

http://127.0.0.1/item/deleteItem.action?id=1 刪除,GET或POST

實際程式碼:

實際效果:

使用RestFul

使用RESTful操作資源 :可以通過不同的請求方式來實現不同的效果

http://127.0.0.1/item/1 查詢,GET

http://127.0.0.1/item 新增,POST

http://127.0.0.1/item 更新,PUT

http://127.0.0.1/item/1 刪除,DELETE

實際測試:

程式碼:

實際效果圖:

路徑變數的優點

(1)使路徑變得更加簡潔;

(2)獲得引數更加方便,框架會自動進行型別轉換。

(3)通過路徑變數的型別可以約束訪問引數,如果型別不一樣,則訪問不到對應的請求方法,

深入測試

//對映訪問路徑
@RequestMapping("/commit/{p1}/{p2}")
public String index(@PathVariable int p1, @PathVariable String p2, Model model){

   String result = p1+p2;
   //Spring MVC會自動例項化一個Model物件用於向檢視中傳值
   model.addAttribute("msg", "結果:"+result);
   //返回檢視位置
   return "test";

}

使用method屬性指定請求型別

用於約束請求的型別,可以收窄請求範圍。指定請求謂詞的型別如GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE等

//對映訪問路徑,必須是POST請求
@RequestMapping(value = "/hello",method = {RequestMethod.POST})
public String index2(Model model){
   model.addAttribute("msg", "hello!");
   return "test";
}

如果使用瀏覽器位址列進行訪問預設是Get請求,會報錯405:

解法:將POST修改為GET則正常了;

//對映訪問路徑,必須是Get請求
@RequestMapping(value = "/hello",method = {RequestMethod.GET})
public String index2(Model model){
   model.addAttribute("msg", "hello!");
   return "test";
}

總結

(1)Spring MVC 的 @RequestMapping 註解能夠處理 HTTP 請求的方法(GET, PUT, POST, DELETE ,PATCH)。所有的位址列請求預設都會是 HTTP GET 型別的。

(2)方法級別的註解變體有如下幾個:組合註解

@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping

@GetMapping 是一個組合註解,常用,實質 @RequestMapping(method =RequestMethod.GET) 的一個快捷方式。

結果跳轉方式

ModelAndView

設定ModelAndView物件 , 根據view的名稱 , 和檢視解析器跳到指定的頁面 .

頁面 : {檢視解析器字首} + viewName +{檢視解析器字尾}

<!-- 檢視解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
     id="internalResourceViewResolver">
   <!-- 字首 -->
   <property name="prefix" value="/WEB-INF/jsp/" />
   <!-- 字尾 -->
   <property name="suffix" value=".jsp" />
</bean>

對應的controller類

public class ControllerTest1 implements Controller {

   public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
       //返回一個模型檢視物件
       ModelAndView mv = new ModelAndView();
       mv.addObject("msg","ControllerTest1");
       mv.setViewName("test");
       return mv;
  }
}

ServletAPI

通過設定ServletAPI , 不需要檢視解析器 .

1、通過HttpServletResponse進行輸出

2、通過HttpServletResponse實現重定向

3、通過HttpServletResponse實現轉發

@Controller
public class ResultGo {

   @RequestMapping("/result/t1")
   public void test1(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
       rsp.getWriter().println("Hello,Spring BY servlet API");
  }

   @RequestMapping("/result/t2")
   public void test2(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
       rsp.sendRedirect("/index.jsp");
  }

   @RequestMapping("/result/t3")
   public void test3(HttpServletRequest req, HttpServletResponse rsp) throws Exception {
       //轉發
       req.setAttribute("msg","/result/t3");
       req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,rsp);
  }

}

SpringMVC(重要)

通過SpringMVC來實現轉發和重定向 - 無需檢視解析器;

測試前,需要將檢視解析器註釋掉

注意區分轉發與重定向

@Controller
public class ResultSpringMVC {
   @RequestMapping("/rsm/t1")
   public String test1(){
       //轉發
       return "/index.jsp";
  }

   @RequestMapping("/rsm/t2")
   public String test2(){
       //轉發二
       return "forward:/index.jsp";
  }

   @RequestMapping("/rsm/t3")
   public String test3(){
       //重定向
       return "redirect:/index.jsp";
  }
}

通過SpringMVC來實現轉發和重定向 - 有檢視解析器;

重定向不需要檢視解析器 , 本質就是重新請求一個新地方 , 所以注意路徑問題.

可以重定向到另外一個請求實現 .

Tips:

重定向不能訪問WEB-INF下的資源
轉發才會觸發檢視解析器拼接,重定向不會觸發

@Controller
public class ResultSpringMVC2 {
   @RequestMapping("/rsm2/t1")
   public String test1(){
       //轉發
       return "test";
  }

   @RequestMapping("/rsm2/t2")
   public String test2(){
       //重定向
       return "redirect:/index.jsp";
       //return "redirect:hello.do"; //hello.do為另一個請求/
  }

}

資料處理

處理提交資料

1、提交的域名稱和處理方法的引數名一致

提交資料 : http://localhost:8080/hello?name=lzh

處理方法 :

@RequestMapping("/hello")
public String hello(String name){
   System.out.println(name);
   return "hello";
}

實際程式碼:

後臺輸出 : lzh

2、提交的域名稱和處理方法的引數名不一致

username提交的域的名稱
name處理方法的引數名

提交資料 : http://localhost:8080/hello?username=lzhCreate

處理方法 :

//@RequestParam("username") : username提交的域的名稱 .
@RequestMapping("/hello")
public String hello(@RequestParam("username") String name){
   System.out.println(name);
   return "hello";
}

後臺輸出 : lzhCreate

3、提交的是一個物件

要求提交的表單域和物件的屬性名一致 , 引數使用物件即可

關於lombok再述
@Data 只有get post toString()
所以常在下補充

@AllArgsConstructor
@NoArgsConstructor

1、實體類

public class User {
   private int id;
   private String name;
   private int age;
   //構造
   //get/set
   //tostring()
}

2、提交資料 : http://localhost:8080/mvc04/user?name=lzh&id=1&age=15

3、處理方法 :

@RequestMapping("/user")
public String user(User user){
   System.out.println(user);
   return "hello";
}

後臺輸出 : User { id=1, name='lzh', age=15 }

注意i:如果使用物件的話,前端傳遞的引數名和物件名必須一致,否則就是null。

資料顯示到前端

第一種 : 通過ModelAndView

public class ControllerTest1 implements Controller {

   public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
       //返回一個模型檢視物件
       ModelAndView mv = new ModelAndView();
       mv.addObject("msg","ControllerTest1");
       mv.setViewName("test");
       return mv;
  }
}

第二種 : 通過ModelMap

@RequestMapping("/hello")
public String hello(@RequestParam("username") String name, ModelMap model){
   //封裝要顯示到檢視中的資料
   //相當於req.setAttribute("name",name);
   model.addAttribute("name",name);
   System.out.println(name);
   return "hello";
}

加上引數@RequestParam("username")代表一定從前端所接收
第三種 : 通過Model

第三種:Model

@RequestMapping("/ct2/hello")
public String hello(@RequestParam("username") String name, Model model){
   //封裝要顯示到檢視中的資料
   //相當於req.setAttribute("name",name);
   model.addAttribute("msg",name);
   System.out.println(name);
   return "test";
}

三者對比總結

Model只有幾個方法只適合用於儲存資料,簡化了對於Model物件的操作和理解;

ModelMap 繼承了 LinkedMap ,除了實現了自身的一些方法,同樣的繼承 LinkedMap 的方法和特性;

ModelAndView 可以在儲存資料的同時,可以進行設定返回的邏輯檢視,進行控制展示層的跳轉。(不常用)

常見異常即錯誤分析

①頁面無報錯但不顯示資料



②tomcat 輸出控制檯亂碼問題


解決:在tomcat的VM配置加上-Dfile.encoding=UTF-8

結果:

基於實戰中學習,學習快樂中成長
.
時間會回答成長,成長會回答夢想