SpringBoot視圖解析實(shí)現(xiàn)原理深入分析
一、寫在前面
前面我們分析了Spring boot是如何解析請(qǐng)求參數(shù)和如何處理相應(yīng)信息的
那么它是如何進(jìn)行視圖解析,找到我們要跳轉(zhuǎn)的視圖并進(jìn)行視圖渲染的呢?
二、寫個(gè)demo
為了研究視圖解析原理,我們寫一個(gè)測(cè)試代碼
首先是控制器類IndexController.java
package com.decade.controller;
import com.decade.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import javax.servlet.http.HttpSession;
@Controller
public class IndexController {
/**
* 來登錄頁
* @return 跳轉(zhuǎn)登錄頁
*/
@GetMapping(value = {"/","/login"})
public String loginPage(){
return "login";
}
@PostMapping("/login")
public String main(User user, HttpSession session, Model model){
if(StringUtils.hasLength(user.getUserName()) && "123456".equals(user.getPassword())){
//把登陸成功的用戶保存到session中
session.setAttribute("loginUser", user);
//登錄成功重定向到main.html; 重定向防止刷新首頁時(shí)表單重復(fù)提交
return "redirect:/main.html";
}else {
model.addAttribute("msg","賬號(hào)密碼錯(cuò)誤");
//回到登錄頁面
return "login";
}
}
/**
* 去main頁面
* @return 跳轉(zhuǎn)主頁面
*/
@GetMapping("/main.html")
public String mainPage(HttpSession session,Model model){
//是否登錄。 攔截器,過濾器
Object loginUser = session.getAttribute("loginUser");
if(loginUser != null){
return "main";
}else {
//回到登錄頁面
model.addAttribute("msg","請(qǐng)重新登錄");
return "login";
}
}
}login.html放于templates文件夾下
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
<meta name="description" content="">
<meta name="author" content="ThemeBucket">
<link rel="shortcut icon" href="#" rel="external nofollow" type="image/png">
<title>Login</title>
<link href="css/style.css" rel="external nofollow" rel="stylesheet">
<link href="css/style-responsive.css" rel="external nofollow" rel="stylesheet">
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="js/html5shiv.js"></script>
<script src="js/respond.min.js"></script>
<![endif]-->
</head>
<body class="login-body">
<div class="container">
<form class="form-signin" action="index.html" method="post" th:action="@{/login}">
<div class="form-signin-heading text-center">
<h1 class="sign-title">Sign In</h1>
<img src="images/login-logo.png" alt=""/>
</div>
<div class="login-wrap">
<label style="color: red" th:text="${msg}"></label>
<input type="text" name="userName" class="form-control" placeholder="User ID" autofocus>
<input type="password" name="password" class="form-control" placeholder="Password">
<button class="btn btn-lg btn-login btn-block" type="submit">
<i class="fa fa-check"></i>
</button>
<div class="registration">
Not a member yet?
<a class="" href="registration.html" rel="external nofollow" >
Signup
</a>
</div>
<label class="checkbox">
<input type="checkbox" value="remember-me"> Remember me
<span class="pull-right">
<a data-toggle="modal" href="#myModal" rel="external nofollow" > Forgot Password?</a>
</span>
</label>
</div>
<!-- Modal -->
<div aria-hidden="true" aria-labelledby="myModalLabel" role="dialog" tabindex="-1" id="myModal" class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title">Forgot Password ?</h4>
</div>
<div class="modal-body">
<p>Enter your e-mail address below to reset your password.</p>
<input type="text" name="email" placeholder="Email" autocomplete="off" class="form-control placeholder-no-fix">
</div>
<div class="modal-footer">
<button data-dismiss="modal" class="btn btn-default" type="button">Cancel</button>
<button class="btn btn-primary" type="button">Submit</button>
</div>
</div>
</div>
</div>
<!-- modal -->
</form>
</div>
<script src="js/jquery-1.10.2.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<script src="js/modernizr.min.js"></script>
</body>
</html>三、流程解析
我們?cè)诘卿涰撾S機(jī)輸入用戶名和錯(cuò)誤的密碼,然后點(diǎn)擊登錄
我們跟蹤斷點(diǎn),他還是進(jìn)入到DispatcherServlet這個(gè)類下的doDispatch()方法

然后我們走到RequestMappingHandlerAdapter類下的handleInternal()—>invokeHandlerMethod()
設(shè)置參數(shù)解析器和返回值處理器

在這里,它就會(huì)走到ServletInvocableHandlerMethod類下的invokeAndHandle()方法,通過this.invokeForRequest(webRequest, mavContainer, providedArgs)去調(diào)用控制器類中的具體方法
我們發(fā)現(xiàn),如果方法的參數(shù)是一個(gè)自定義類型對(duì)象(例如demo中的User),調(diào)用完具體方法后,我們會(huì)把他放在ModelAndViewContainer中

斷點(diǎn)繼續(xù)深入,我們走到了HandlerMethodReturnValueHandlerComposite類下的handleReturnValue(),這里它會(huì)使用this.selectHandler(returnValue, returnType)去獲取自己適合的返回值解析器

然后我們進(jìn)入到ViewNameMethodReturnValueHandler這個(gè)返回值解析器
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue instanceof CharSequence) {
// 獲取視圖名稱
String viewName = returnValue.toString();
mavContainer.setViewName(viewName);
// 判斷是否是重定向頁面
if (this.isRedirectViewName(viewName)) {
mavContainer.setRedirectModelScenario(true);
}
} else if (returnValue != null) {
throw new UnsupportedOperationException("Unexpected return type: " + returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
}
}重點(diǎn):我們可以發(fā)現(xiàn),目標(biāo)方法處理的過程中,所有數(shù)據(jù)都會(huì)被放在 ModelAndViewContainer 里面。包括數(shù)據(jù)和視圖地址
經(jīng)過上述處理之后,我們返回到RequestMappingHandlerAdapter類中
ModelAndView var15 = this.getModelAndView(mavContainer, modelFactory, webRequest);
這行代碼會(huì)將ModelAndViewContainer 中的數(shù)據(jù)放到ModelAndView中
然后返回到DispatcherServlet這個(gè)類中,處理派發(fā)結(jié)果(頁面該如何響應(yīng))的關(guān)鍵代碼this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
我們進(jìn)入該方法進(jìn)行分析
this.render(mv, request, response); 進(jìn)行頁面渲染邏輯
String viewName = mv.getViewName();獲取視圖名稱
view = this.resolveViewName(viewName, mv.getModelInternal(), locale, request);循環(huán)遍歷當(dāng)前系統(tǒng)中所有的視圖解析器嘗試是否能根據(jù)當(dāng)前返回值得到View對(duì)象,符合條件的視圖解析器為ContentNegotiatingViewResolver(ContentNegotiationViewResolver 里面包含了下面所有的視圖解析器,內(nèi)部還是利用下面所有視圖解析器得到視圖對(duì)象),最后返回的視圖對(duì)象為ThymeleafView

下面是幾種不同返回值所對(duì)應(yīng)的視圖對(duì)象
- 返回值以 forward: 開始: new InternalResourceView(forwardUrl); —> 轉(zhuǎn)發(fā),底層為request.getRequestDispatcher(path).forward(request, response);
- 返回值以 redirect: 開始: new RedirectView() —> render底層就是重定向
- 返回值是普通字符串: new ThymeleafView()—>通過對(duì)應(yīng)的解析處理,并通過writer輸出流寫出到頁面

然后通過view.render(mv.getModelInternal(), request, response);調(diào)用視圖對(duì)象自定義的render進(jìn)行頁面渲染工作
大家也可以輸入正確的用戶名和密碼,debug一下重定向是如何解析的
到此這篇關(guān)于SpringBoot視圖解析實(shí)現(xiàn)原理深入分析的文章就介紹到這了,更多相關(guān)SpringBoot視圖解析內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JAVA Iterator接口與增強(qiáng)for循環(huán)的實(shí)現(xiàn)
這篇文章主要介紹了JAVA Iterator接口與增強(qiáng)for循環(huán)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
Spring Boot使用RestTemplate消費(fèi)REST服務(wù)的幾個(gè)問題記錄
這篇文章主要介紹了Spring Boot使用RestTemplate消費(fèi)REST服務(wù)的幾個(gè)問題記錄,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-06-06
idea中導(dǎo)入別人的springboot項(xiàng)目的方法(圖文)
這篇文章主要介紹了idea中導(dǎo)入別人的springboot項(xiàng)目的方法(圖文),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
MyBatis Plus關(guān)閉SQL日志打印的方法
這篇文章主要介紹了MyBatis-Plus如何關(guān)閉SQL日志打印,文中通過圖文結(jié)合講解的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2024-02-02
SpringBoot實(shí)現(xiàn)定時(shí)發(fā)送郵件的三種方法案例詳解
這篇文章主要介紹了SpringBoot三種方法實(shí)現(xiàn)定時(shí)發(fā)送郵件的案例,Spring框架的定時(shí)任務(wù)調(diào)度功能支持配置和注解兩種方式Spring?Boot在Spring框架的基礎(chǔ)上實(shí)現(xiàn)了繼承,并對(duì)其中基于注解方式的定時(shí)任務(wù)實(shí)現(xiàn)了非常好的支持,本文給大家詳細(xì)講解,需要的朋友可以參考下2023-03-03
spring AOP的Around增強(qiáng)實(shí)現(xiàn)方法分析
這篇文章主要介紹了spring AOP的Around增強(qiáng)實(shí)現(xiàn)方法,結(jié)合實(shí)例形式分析了spring面向切面AOP的Around增強(qiáng)具體步驟與相關(guān)操作方法,需要的朋友可以參考下2020-01-01

