摘要: spring MVC這個環境中,Spring MVC會依據controller(或者你叫它handler)中處理方法的返回值,進行解析,解析之后提供一個視圖,作為響應。 標注了@Controller的處理器,實際上本質是一個POJO,你標注了@Controller,我就高看你一眼。
spring MVC這個環境中,Spring MVC會依據controller(或者你叫它handler)中處理方法的返回值,進行解析,解析之后提供一個視圖,作為響應。
標注了@Controller的處理器,實際上本質是一個POJO,你標注了@Controller,我就高看你一眼。但你的形態就是一個java代碼文件。
你作為一個java的土土的文件,你里面處理方法的返回值,也就是return語句,返回了一個東西。這個東西可以是String 也可以是 ModelAndView對象。這就是標注了@Controller的方法的全部工作。
接下來,Spring容器(或者說Spring MVC容器)需要接着你拋來的返回值,不管你的返回值是String還是ModelAndView,我,作為一個容器,我全都封裝成ModelAndView對象。然后,我,Spring容器的一部分,視圖解析器,開始工作。
視圖解析器的英文名字叫做 ViewResolver,這個東西首先是Spring定義得人一個接口,具體你的Spring容器中的視圖解析器有怎樣的功能,取決於你為你自己的Spring容器配置了哪種具體的Spring視圖解析器的實現類。
看看之前我們看過的一個圖:
這個是spring mvc 的jar中的默認配置
當然你的spring項目也可以在配置文件中覆蓋上述配置(我並沒有用別的視圖解析器取代默認的InternalResourceViewResolver):
/** * @return 登錄驗證 */ @RequestMapping("/dologin") public ModelAndView dologin(HttpServletRequest request, User user) { User us1 = uss.getUserByName(user.getSrName()); ModelAndView mav = new ModelAndView(); mav.setViewName("forward:/login.jsp"); if (us1 == null) { mav.addObject("errorMsg", "用戶名不存在"); } else if (!us1.getSrPwd().equals(user.getSrPwd())) { mav.addObject("errorMsg", "用戶密碼不正確"); } else { } return mav; }
@Controller中的方法返回值最終都是ModelAndView,我們需要搞清楚兩件事:
1.ModelAndView是什么?
2.視圖解析器究竟做了哪些工作,才能返回我們需要的視圖?
我們應該先看看ModelAndView是怎么回事:
ModelAndView是Spring中標准的類,完全是Spring自己封裝的對象。Spring API中如此描述這個對象:
public class ModelAndView extends java.lang.Object
Holder for both Model and View in the web MVC framework. Note that these are entirely distinct. This class merely holds both to make it possible for a controller to return both model and view in a single return value.
Represents a model and view returned by a handler, to be resolved by a DispatcherServlet. The view can take the form of a String view name which will need to be resolved by a ViewResolver object; alternatively a View object can be specified directly. The model is a Map, allowing the use of multiple objects keyed by name.
用人話解釋一下ModelAndView是干什么用的,ModelAndView包含兩部分:一個View和一個Model
View由setViewName()方法來決定,決定讓ViewResolver去哪里找View文件,並找到是哪個jsp文件;
Model由addObject()方法來決定,它的本質是java的HashMap,鍵值對;
用人話來解釋ModelAndView的功能就是,View負責渲染Model,讓你找到代表View的jsp,用這個jsp去渲染Model中的數據。
看看Spring源碼:
Spring官網提供的API
去這個路徑找一下:
也就是說ModelAndView對象中的核心成員就是Object和ModelMap
其中ModelMap也是Spring自己定義的對象。
ModelMap的本質是Java的標准容器:LinkedHashMap
屬性成員我們已經搞清楚了,下面是方法成員:
setViewName()方法和addObject()方法
public void setViewName(@Nullable String viewName) { this.view = viewName; } public ModelAndView addObject(String attributeName, Object attributeValue) { getModelMap().addAttribute(attributeName, attributeValue); return this; } public ModelAndView addObject(Object attributeValue) { getModelMap().addAttribute(attributeValue); return this; } public ModelMap getModelMap() { if (this.model == null) { this.model = new ModelMap(); } return this.model; }
也就是說,ModelAndView對象沒有什么神秘之處,解構一下核心就是Object\LinkedHashMap,完全是Java的標准容器(對象)。
也就是說,關鍵不在於ModelAndView對象,而在於“視圖解析器”這個Spring容器的核心部件。
那么視圖解析器怎樣工作呢?
你明明知道你用的ViewResolver的實現類就是InternalResourceViewResolver,那么你應該仔細看看Spring API中這一部分的詳細內容:
https://docs.spring.io/spring/docs/5.0.1.RELEASE/javadoc-api/
首先InternalResourceViewResolver extends(繼承)了 UrlBasedViewResolver;
然后順便說,把用於顯示(view)的jsp文件放在WEB-INF文件夾下是一種安全的做法,這樣不能通過url直接access這些jsp,只能通過Controller java類來訪問它們。
於是我們繼續去看UrlBasedViewResolver
我想這樣一個Spring官方的API中的說明性文字已經足以解開所有疑惑:那就是ModelAndView對象的方法setViewName()中的參數,看上去像是一個普通字符串的參數,究竟應該采用哪種格式?應該怎么寫?已經有了定論。
As a special feature, redirect URLs can be specified via the "redirect:" prefix. E.g.: "redirect:myAction.do" will trigger a redirect to the given URL, rather than resolution as standard view name. This is typically used for redirecting to a controller URL after finishing a form workflow.
Furthermore, forward URLs can be specified via the "forward:" prefix. E.g.: "forward:myAction.do" will trigger a forward to the given URL, rather than resolution as standard view name. This is typically used for controller URLs; it is not supposed to be used for JSP URLs - use logical view names there