SpringMVC
SpringMVC原理
SpringMVC請求發送到返回請求的全過程
流程:
-
客戶端發送一個請求,Tomcat獲得這個請求後將其做了一個映射判斷(
<url-pattern>/*</url-pattern>
,如果訪問地址符合/*,則交給DisPatcherServlet )傳給DispatcherServlet(前端控制器) -
DispatcherServlet會根據請求去 HandlerMapping查找Handler(可以根據xml配置、註解@RequestMapping進行查找 )生成處理器對象及處理器攔截器(如果有則生成)一併返回給DispatcherServlet.
-
DispatcherServlet調用HandlerAdaptor執行對應的處理器(Handler/Controller),並將請求傳來的參數傳給Handler,
-
Handler(後端控制器)通過Service調用數據庫找到對應的信息然後返回給Handler
-
Handler將ModelAndView返回給HandlerAdaptor
當Handler在完成邏輯處理後,通常會產生一些信息,這些信息就是需要返回給用戶並在瀏覽器上顯示的信息,它們被稱爲模型(Model)。僅僅返回原始的信息時不夠的——這些信息需要以用戶友好的方式進行格式化,一般會是 HTML,所以,信息需要發送給一個視圖(view),通常會是 JSP。
Handler所做的最後一件事就是將模型數據打包,並且表示出用於渲染輸出的視圖名(即代碼中:
mav.setViewName("index")
的index,就是邏輯視圖名)(邏輯視圖名)。它接下來會將請求連同ModelAndView發送回HandlerAdaptor。 -
HandlerAdaptor再將ModelAndView返回給DispatcherServlet,
-
但是DispatcherServlet不會處理這個ModelAndView,所以將其傳給ViewResolver進行解析,ViewResolver根據邏輯視圖名稱解析真正的視圖 ,並返回給DispatcherServlet。
這樣以來,控制器就不會和特定的視圖相耦合,傳遞給 DispatcherServlet 的視圖名並不直接表示某個特定的 JSP。(實際上,它甚至不能確定視圖就是 JSP)相反,它傳遞的僅僅是一個邏輯名稱,這個名稱將會用來查找產生結果的真正視圖(給邏輯視圖名拼接前綴和後綴, 進而確定一個 Web 應用中視圖資源的物理路徑 )
-
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