菜鳥學SpringMVC之——SpringMVC執行原理、向前端返回數據的三種方式

SpringMVC

SpringMVC原理

SpringMVC請求發送到返回請求的全過程

在這裏插入圖片描述

在這裏插入圖片描述

流程:

  1. 客戶端發送一個請求,Tomcat獲得這個請求後將其做了一個映射判斷(<url-pattern>/*</url-pattern>,如果訪問地址符合/*,則交給DisPatcherServlet )傳給DispatcherServlet(前端控制器)

  2. DispatcherServlet會根據請求去 HandlerMapping查找Handler(可以根據xml配置、註解@RequestMapping進行查找 )生成處理器對象及處理器攔截器(如果有則生成)一併返回給DispatcherServlet.

  3. DispatcherServlet調用HandlerAdaptor執行對應的處理器(Handler/Controller),並將請求傳來的參數傳給Handler,

  4. Handler(後端控制器)通過Service調用數據庫找到對應的信息然後返回給Handler

  5. Handler將ModelAndView返回給HandlerAdaptor

    當Handler在完成邏輯處理後,通常會產生一些信息,這些信息就是需要返回給用戶並在瀏覽器上顯示的信息,它們被稱爲模型(Model)。僅僅返回原始的信息時不夠的——這些信息需要以用戶友好的方式進行格式化,一般會是 HTML,所以,信息需要發送給一個視圖(view),通常會是 JSP。

    Handler所做的最後一件事就是將模型數據打包,並且表示出用於渲染輸出的視圖名(即代碼中:mav.setViewName("index")的index,就是邏輯視圖名)(邏輯視圖名)。它接下來會將請求連同ModelAndView發送回HandlerAdaptor。

  6. HandlerAdaptor再將ModelAndView返回給DispatcherServlet,

  7. 但是DispatcherServlet不會處理這個ModelAndView,所以將其傳給ViewResolver進行解析,ViewResolver根據邏輯視圖名稱解析真正的視圖 ,並返回給DispatcherServlet。

    這樣以來,控制器就不會和特定的視圖相耦合,傳遞給 DispatcherServlet 的視圖名並不直接表示某個特定的 JSP。(實際上,它甚至不能確定視圖就是 JSP)相反,它傳遞的僅僅是一個邏輯名稱,這個名稱將會用來查找產生結果的真正視圖(給邏輯視圖名拼接前綴和後綴, 進而確定一個 Web 應用中視圖資源的物理路徑 )

    在這裏插入圖片描述

  8. DispatcherServlet進行視圖渲染,就是將Model填充到Response中顯示在view上。最後在傳到前端

上面的這些組件中,只有Handler和View需要程序員開發。 Handler 是繼DispatcherServlet前端控制器的後端控制器,在DispatcherServlet的控制下Handler對具體的用戶請求進行處理。 View是一個接口,實現類支持不同的View類型(jstlView、freemarkerView、pdfView…)

源碼

HandlerMapping(處理器映射器):根據配置或註解找到最重要執行的Handler

  • HandlerMapping接口的實現類:

    SimpleUrlHandlerMapping類通過配置文件把URL映射到Controller類。

    DefaultAnnotationHandlerMapping類通過註解把URL映射到Controller類。

HandlerAdapter(處理器適配器):幫助DispatcherServlet處理映射請求處理程序的適配器,而不用考慮實際調用的是 哪個處理程序

  • AnnotationMethodHandlerAdapter:通過註解,把請求URL映射到Controller類的方法上。

ViewResolver: 它接受一個由 DispaterServlet 傳遞過來的邏輯視圖名來拼裝爲物理視圖名即具體的頁面地址。

  • UrlBasedViewResolver類 通過配置文件,把一個視圖名交給到一個View來處理。

向前端返回數據的三種方式

瞭解了SpringMVC的原理後接下來看一下怎麼同SpringMVC給前端返回數據(有三種方式)

@Controller
@RequestMapping("/user")
public class UserController {

    @Autowired
    IUserService service;

    @RequestMapping(value = "/login.do", method = {RequestMethod.GET})
    public String login(User user) {
        User result = service.login(user);
        //登錄是否成功就看result是否有返回值
//        System.out.println("判斷登錄是否成功" + result.getId());
        return result.getId() + "";
    }
}

在這裏插入圖片描述
所以這裏報錯的原因就是:

代碼中返回一個String,這個返回的String就代表ModelAndView的View的邏輯視圖名,然後交給ViewResolver,進行解析返回給DispatcherServlet,打開路徑/user/1下的視圖,DispatcherServlet發現並找不到這個路徑,所以返回404。現在就需要讓視圖解析器將1當作文字內容處理,而不要當作視圖解析處理。

向前端返回數據的第一種方式:直接返回字符串

加註解@ResponseBody,將返回值當作文字處理。

@Controller
@RequestMapping("/user")
public class UserController {

    @Autowired
    IUserService service;

    @RequestMapping(value = "/login.do", method = {RequestMethod.GET})
    @ResponseBody
    public String login(User user) {
        User result = service.login(user);
        return result.getId() + "";
    }
}

在這裏插入圖片描述

也可以返回一個對象。它會將這個對象當作json格式返回給前端

@Controller
@RequestMapping("/user")
public class UserController {

    @Autowired
    IUserService service;

    @RequestMapping(value = "/login.do", method = {RequestMethod.GET})
    @ResponseBody
    public User login(User user) {
        User result = service.login(user);
        return result;
    }
}

但是配置文件需要稍加修改,打開mvc註解驅動,添加mvc:annotation-driven標籤,它的作用就是讓@ResponseBody等其他註解生效

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd
">
	<!--mvc註解驅動-->
    <mvc:annotation-driven></mvc:annotation-driven>
	<!--其他配置省略-->
</beans>

導入依賴jar包

    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.62</version>
    </dependency>

所以以後向前端傳json就很簡單。


接下來如果要直接打開一個靜態頁面將不能打開,是因爲DispatcherServlet將他攔截處理了,但是靜態資源根本不需要他來處理,這麼來修改:

  • 方式一:改web.xml文件,讓他只過濾.do的請求(<url-pattern>*.do</url-pattern>),這時index.html就不會攔截。

    配置成*.do表示請求發到Tomcat後,Tomcat發現是.html請求,所以不用給DispatcherServlet,所以Tomcat直接到對應文件夾去找對應的文件返回給前端

  • 還有一種改法,這個web.xml文件不改動,在webapp下創建static文件夾

在這裏插入圖片描述
​ 然後修改applicationContext文件,添加mvc:resources標籤,這個標籤的作用就是爲資源文件(html、傳css、js)做映射。

比如我們當前的這個例子中配置的,DispatcherServlet將不會攔截以/pages開頭的所有請求路徑,並將其當作靜態資源交由Servlet處理

<mvc:resources mapping="/pages/**" location="/static/"></mvc:resources><!--表示前端請求的pages下的所有內容去static下面去找-->

因爲web中配置的是隻要是/*的請求都會先傳給DispatcherServlet,所以不管是請求頁面還是接口,都將傳給DispatcherServlet,DispatcherServlet拿到後發現Spring中配置了mvc:resources,所以他就不用管了,直接到對應的文件目錄下找到對應文件將其返回給前端

向前端返回數據的方式二:接下來實現重定向

@Controller
@RequestMapping("/user")
public class UserController {
    @Autowired
    IUserService service;
    @RequestMapping(value = "/login.do", method = {RequestMethod.GET})

    public String login(User user) {
        User result = service.login(user);
        if (result != null) {
            return "redirect:/pages/index.html";
        } else {
            return "redirect:/pages/error.html";
        }
    }
}

發送請求:http://localhost:8080/user/login.do?username=zaq&pwd=123,看地址欄變化了,重定向

在這裏插入圖片描述

向前端返回數據的方式三:接下來實現轉發

轉發是有可能攜帶數據的,那麼怎麼把數據帶入到對應的頁面中去,在對應的頁面又怎麼填充這些數據呢。一般轉發都用的是jsp頁面,jsp頁面展示的時候填充對應的數據。

轉發的時候直接返回頁面位置就行了

@RequestMapping(value = "/login2.do", method = {RequestMethod.GET})
//    @ResponseBody
    public String login2(User user) {
        User result = service.login(user);
        return "jsp/success.jsp";//這時就會做轉發動作
    }

但是要注意配置DispatcherServlet不能過濾它,即配置<url-pattern>*.do</url-pattern>,這時它就不會被視圖解析器解析

注意地址欄地址沒有改變,所以是轉發

在這裏插入圖片描述
但是在開發過程中出於某種原因,就是不能設置爲只過濾*.do,還是要過濾所有的請求,則直接設置<url-pattern>/</url-pattern>就行了,(注意不能設置爲/*,他還是會攔截.jsp文件的)

還要在Spring的配置文件中加入資源解析器。

<!--這裏配置了一個 Spring MVC 內置的一個視圖解析器,
    該解析器是遵循着一種約定:會在邏輯視圖名上添加前綴和後綴,
    進而確定一個 Web 應用中視圖資源的物理路徑-->
<mvc:view-resolvers>
    <mvc:jsp prefix="/jsp/" suffix=".jsp" view-class="org.springframework.web.servlet.view.JstlView"></mvc:jsp>
</mvc:view-resolvers>
@RequestMapping(value = "/login2.do", method = {RequestMethod.GET})
//    @ResponseBody
    public String login2(User user) {
        User result = service.login(user);
        return "success";//注意這時就不要寫物理路徑了,只需要寫邏輯視圖名就行了。
    }

總結:

SpringMVC接收前端傳來的數據,直接通過方法的參數直接接收

SpringMVC向前端返回數據的三種方式:

  • 返回字符串:@ResponseBody

  • 重定向:返回字符串,以redirect: 開頭

  • 轉發:

    • 如果返回的時候不帶數據,直接返回字符串,直接寫路徑。

    • 如果返回的時候要帶數據,則返回ModelAndView

          @RequestMapping(value = "/login2.do", method = {RequestMethod.GET})
          public ModelAndView login2(User user) {
              User result = service.login(user);
              ModelAndView mav = new ModelAndView();
              mav.addObject("result",result);//要返回的數據添加到這裏,返回幾個對象,則添加幾個。
              mav.setViewName("success");//這就相當於原來要直接返回的String,即邏輯視圖名
              return mav;
          }
      

      在JSP頁面中獲取

      <body>
          成功登錄,這個是一個JSP頁面 <%=((User) request.getAttribute("result")).getUsername()%>
      </body>
      </html>
      

部分內容參考:
https://www.iteye.com/blog/elf8848-875830
https://www.jianshu.com/p/91a2d0a1e45a
https://www.cnblogs.com/selene/p/4658554.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章