SpringMVC執(zhí)行流程有哪些,針對這個問題,這篇文章詳細介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
成都創(chuàng)新互聯(lián)長期為超過千家客戶提供的網(wǎng)站建設(shè)服務(wù),團隊從業(yè)經(jīng)驗10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為思南企業(yè)提供專業(yè)的網(wǎng)站設(shè)計、網(wǎng)站建設(shè),思南網(wǎng)站改版等技術(shù)服務(wù)。擁有10余年豐富建站經(jīng)驗和眾多成功案例,為您定制開發(fā)。
還是之前的三個套路
Spring提供一套視圖層的處理框架,他基于Servlet實現(xiàn),可以通過XML或者注解進行我們需要的配置。
他提供了攔截器,文件上傳,CORS等服務(wù)。
原生Servlet在大型項目中需要進過多重封裝,來避免代碼冗余,其次由于不同接口需要的參數(shù)不同,我們需要自己在Servlet層 封裝我們需要的參數(shù),這對于開發(fā)者來說是一種重復(fù)且枯燥的工作,于是出現(xiàn)了視圖層框架,為我們進行參數(shù)封裝等功能。讓開發(fā)者的注意力全部放在邏輯架構(gòu)中,不需要考慮參數(shù)封裝等問題。
再聊怎么用之前,我們需要了解一下MVC的工作原理。
他基于一個DispatcherServlet類實現(xiàn)對各種請求的轉(zhuǎn)發(fā),即前端的所有請求都會來到這個Servlet中,然后這個類進行參數(shù)封裝和請求轉(zhuǎn)發(fā),執(zhí)行具體的邏輯。(第二章我們細聊)
根據(jù)上面的原理,我們需要一個DispatcherServlet來為我們提供基礎(chǔ)的Servlet服務(wù),我們可以通過servlet規(guī)范的web.xml文件,對該類進行初始化。并且聲明該類處理所有的請求,然后通過這個類實現(xiàn)請求轉(zhuǎn)發(fā)。
另外,我們還需要一個配置文件,用來配置我們需要的相關(guān)的mvc信息。
下面來看一個完整的web.xml配置
<web-app> <servlet> <servlet-name>dispatchServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatchServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
注解方式也是現(xiàn)在主流,SpringBoot基于JavaConfig實現(xiàn)了自動配置
實現(xiàn)方式:
在Servlet3.0的時候定義了一個規(guī)范SPI規(guī)范。
SPI ,全稱為 Service Provider Interface,是一種服務(wù)發(fā)現(xiàn)機制。它通過在ClassPath路徑下的META-INF/services文件夾查找文件,自動加載文件里所定義的類。也就是在服務(wù)啟動的時候會Servlet會自動加載該文件定義的類

我們看一眼這個文件里的內(nèi)容。他內(nèi)部定義了SpringServletContainerInitializer容器初始化類,也就是說在Servlet啟動的時候會自動初始化這個類,這個類也是注解實現(xiàn)的關(guān)鍵。
這個類中存在一個onStartup方法,這個也是當(dāng)容器初始化的時候調(diào)用的方法,這個方法有兩參數(shù)
Set<class<?>> webAppInitializerClasses他代表了當(dāng)前我們的Spring容器中存在的web初始化類。我們自己可以通過實現(xiàn)WebApplicationInitializer類來自定義Servlet初始化的時候執(zhí)行的方法。
ServletContext servletContex代表了Servlet上下文對象
org.springframework.web.SpringServletContainerInitializer
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(Set<class<?>> webAppInitializerClasses,
ServletContext servletContext) throws ServletException {
//啟動邏輯
}
}具體看一下注解配置方式:
public class MyWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletCxt) {
// Load Spring web application configuration
AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
//一個配置類,@Configuration
ac.register(AppConfig.class);
//spring的那個refresh方法
ac.refresh();
// Create and register the DispatcherServlet
DispatcherServlet servlet = new DispatcherServlet(ac);
ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
registration.setLoadOnStartup(1);
registration.addMapping("/app/*");
}
}通過實現(xiàn)WebApplicationInitializer接口,來作為MVC的配置類,在加載SpringServletContainerInitializer的時候加載這個類。
不過在具體的實現(xiàn)中,Spring不建議我們這樣做,他建議將Spring和SpringMvc分開,看個圖

他在Spring之上加了一層Web環(huán)境配置。相當(dāng)于在Spring的外面包裝了一層Servlet
看一下此時的代碼
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
//Spring配置文件
@Override
protected Class<!--?-->[] getRootConfigClasses() {
return new Class<!--?-->[] { RootConfig.class };
}
//SpringMVC的配置文件
@Override
protected Class<!--?-->[] getServletConfigClasses() {
return new Class<!--?-->[] { App1Config.class };
}
//指定DispatcherServlet可以攔截的路徑
@Override
protected String[] getServletMappings() {
return new String[] { "/app1/*" };
}
}通過AbstractAnnotationConfigDispatcherServletInitializer

可以看到他實現(xiàn)了WebApplicationInitializer接口,即在Servlet初始化的時候會加載這個類。
AbstractContextLoaderInitializer類,他初始化了Spring
AbstractDispatcherServletInitializer類,初始化了DispatcherServlet
AbstractAnnotationConfigDispatcherServletInitializer類,將兩個類整合到一起
聊這個原理之前,先來聊聊他要干什么?
需求:請求分發(fā);參數(shù)封裝;結(jié)果返回
那如果我們自己來實現(xiàn),該怎么辦?(單說注解,先來看看我們怎么使用MVC的)
一個@Controller注解,標識當(dāng)前類為控制層接口,
一個RequestMapping標識這個方法的URI和請求方式等信息
一個@ResponseBody標識這個方法的返回類型為JSON
一個test01標識這個方法用來處理/test請求
@Controller
public class UserController {
@GetMapping("/test")
@ResponseBody
public String test01(){
return "success" ;
}
}接下來,我們通過我們已有的東西,看一下我們自己去處理請求的邏輯
先來想一下我們的請求過程:
前端發(fā)送一個Http請求,通過不同的uri實現(xiàn)不同邏輯的處理
而這個uri和我們后端的定義的@RequestMapping中的value值相同
即我們可以通過一個Map結(jié)構(gòu),將value作為key,將method的Class對象作為一個value存到一個MappingRegister中
請求來了以后,通過URI從這個Map中獲取相應(yīng)的Method執(zhí)行,如果沒有對應(yīng)的Method給一個404.
在上面的怎么用中提到了,他通過AbstractContextLoaderInitializer來加載Spring配置文件的。

此時關(guān)于Spring的東西已經(jīng)加載好了,但并未進行初始化
同樣也是通過AbstractDispatcherServletInitializer類實現(xiàn)

接下來我們具體看一下在這個期間,DispatcherServlet如何處理請求的
作用:分發(fā)所有的請求
類繼承結(jié)構(gòu)圖

可以看到他繼承了HttpServlet類,屬于一個Servlet,而在之前我們配置了這個Servlet的攔截路徑。他會將所有的請求攔截,然后做一個分發(fā)。
下面這個圖各位看官應(yīng)該非常熟悉:

其實DispatcherServlet處理所有請求的方式在這個圖里完全都體現(xiàn)了。
接下來聊一下他的設(shè)計思路吧。
當(dāng)一個請求來的時候,進入doDispatch方法中,然后處理這個請求,也是返回一個執(zhí)行鏈
Spring提供了三種方式的處理器映射器來處理不同的請求。
BeanNameUrlHandlerMapping處理單獨Bean的請求。適用于實現(xiàn)Controller和HttpRequestHandler接口的類
@Component("/test02")
public class HttpController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("HttpController執(zhí)行");
return null;
}
}@Component("/test01")
public class HandlerController implements HttpRequestHandler {
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("handlerRequest");
}
}RequestMappingHandlerMapping適用于方法類型的處理器映射。
@Controller
public class UserController {
@GetMapping("/test")
public String test01(){
System.out.println("執(zhí)行了");
return "success" ;
}
}RouterFunctionMapping,MVC提供的一個處理通過函數(shù)式編程定義控制器的一個映射器處理器。需要直接添加到容器中,然后 通過路由一個地址,返回對應(yīng)的數(shù)據(jù)
@Configuration
@ComponentScan("com.bywlstudio.controller")
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp("/WEB-INF/pages/",".jsp");
}
@Bean
public RouterFunction<!--?--> routerFunctionA() {
return RouterFunctions.route()
.GET("/person/{id}", request1 -> ServerResponse.ok().body("Hello World"))
.build();
}
}聊完了處理器映射器,再來聊一下處理器適配器
不同的請求方式,需要不同的處理方式,這也是Spring為什么要提供一個適配器的原因。
RequestMappingHandlerAdapter用來處理所有的方法請求,即通過@Controller注解定義的
HandlerFunctionAdapter用來處理函數(shù)式的映射,即通過RouterFunctionMapping定義的
HttpRequestHandlerAdapter用來處理實現(xiàn)了HttpRequestHandler接口的
SimpleControllerHandlerAdapter用來處理實現(xiàn)了Controller接口的請求
通過處理器適配器拿到適合的處理器,來處理對應(yīng)的請求。
在處理器執(zhí)行具體的請求的過程,實際上就是調(diào)用我們的方法的過程,于是就會出現(xiàn)返回值
通常對于返回值我們有兩種方法:
@ResponseBody直接返回JSON數(shù)據(jù)。
或者返回一個視圖,該視圖會被視圖解析器解析。
對于返回值解析,MVC提供了一個接口用于處理所有的返回值,這里我們僅僅談上面的兩種
ModelAndViewMethodReturnValueHandler用于處理返回視圖模型的請求
RequestResponseBodyMethodProcessor用于處理返回JSON
在我們拿到方法返回值以后,會調(diào)用this.returnValueHandlers.handleReturnValue返回值解析器的這個方法,用于對視圖模型的返回和JSON數(shù)據(jù)的回顯(直接回顯到網(wǎng)頁,此時返回的視圖對象為null)
對于視圖對象,通過視圖解析器直接解析,進行數(shù)據(jù)模型渲染,然后回顯給前端。
這個類存放了method的映射信息。
class MappingRegistry {
private final Map<t, mappingregistration<t>> registry = new HashMap<>();
private final Map<t, handlermethod> mappingLookup = new LinkedHashMap<>();
private final MultiValueMap<string, t> urlLookup = new LinkedMultiValueMap<>();
private final Map<string, list<handlermethod>> nameLookup = new ConcurrentHashMap<>();
private final Map<handlermethod, corsconfiguration> corsLookup = new ConcurrentHashMap<>();
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();MVC會從這個類中獲取方法和URL的引用。相當(dāng)于Spring MVC的容器。
答:MVC是一個架構(gòu)模式,它有三個核心
視圖(View)。用戶界面
模型(Model)。業(yè)務(wù)數(shù)據(jù)
控制器(Controller)。接收用戶輸入,控制模型和視圖進行數(shù)據(jù)交互
MVVM也是一種架構(gòu)模式,它也是三個核心
模型(Model)。后端數(shù)據(jù)
視圖模型(ViewModel)。它完成了數(shù)據(jù)和視圖的綁定
視圖(View)。用戶界面
它的核心思想是:通過ViewModel將數(shù)據(jù)和視圖綁定,用數(shù)據(jù)操作視圖,常見框架為Vue
用戶發(fā)送請求至DispatcherServlet
DispatcherServelt收到請求以后調(diào)用HandlerMapping,找到請求處理器映射器(三選一)
通過處理器映射器對應(yīng)URI的處理器執(zhí)行鏈(包含了攔截器,和處理器對象)
調(diào)用處理器適配器,找到可以處理該執(zhí)行鏈的處理器(四選一)
處理器具體執(zhí)行,返回ModelAndView對象
如果存在@ResponseBody注解,直接進行數(shù)據(jù)回顯
將返回的ModelAndView對象傳給ViewResove視圖解析器解析,返回視圖
DispatcherServlet對View進行渲染視圖
響應(yīng)用戶
關(guān)于SpringMVC執(zhí)行流程有哪些問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道了解更多相關(guān)知識。
本文標題:SpringMVC執(zhí)行流程有哪些
本文網(wǎng)址:http://www.chinadenli.net/article44/geigee.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供外貿(mào)網(wǎng)站建設(shè)、網(wǎng)站建設(shè)、營銷型網(wǎng)站建設(shè)、軟件開發(fā)、全網(wǎng)營銷推廣、面包屑導(dǎo)航
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)