SpringMVC執(zhí)行流程有哪些

SpringMVC執(zhí)行流程有哪些,針對(duì)這個(gè)問題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問題的小伙伴找到更簡(jiǎn)單易行的方法。

成都創(chuàng)新互聯(lián)長(zhǎng)期為超過千家客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對(duì)不同對(duì)象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺(tái),與合作伙伴共同營(yíng)造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為思南企業(yè)提供專業(yè)的網(wǎng)站設(shè)計(jì)、網(wǎng)站建設(shè)思南網(wǎng)站改版等技術(shù)服務(wù)。擁有10余年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開發(fā)。

MVC總結(jié)

1. 概述

還是之前的三個(gè)套路

1.1 是什么?

Spring提供一套視圖層的處理框架,他基于Servlet實(shí)現(xiàn),可以通過XML或者注解進(jìn)行我們需要的配置。

他提供了攔截器,文件上傳,CORS等服務(wù)。

1.2 為什么用?

原生Servlet在大型項(xiàng)目中需要進(jìn)過多重封裝,來避免代碼冗余,其次由于不同接口需要的參數(shù)不同,我們需要自己在Servlet層 封裝我們需要的參數(shù),這對(duì)于開發(fā)者來說是一種重復(fù)且枯燥的工作,于是出現(xiàn)了視圖層框架,為我們進(jìn)行參數(shù)封裝等功能。讓開發(fā)者的注意力全部放在邏輯架構(gòu)中,不需要考慮參數(shù)封裝等問題。

1.3 怎么用

再聊怎么用之前,我們需要了解一下MVC的工作原理。

他基于一個(gè)DispatcherServlet類實(shí)現(xiàn)對(duì)各種請(qǐng)求的轉(zhuǎn)發(fā),即前端的所有請(qǐng)求都會(huì)來到這個(gè)Servlet中,然后這個(gè)類進(jìn)行參數(shù)封裝和請(qǐng)求轉(zhuǎn)發(fā),執(zhí)行具體的邏輯。(第二章我們細(xì)聊)

1.3.1 XML
  • 根據(jù)上面的原理,我們需要一個(gè)DispatcherServlet來為我們提供基礎(chǔ)的Servlet服務(wù),我們可以通過servlet規(guī)范的web.xml文件,對(duì)該類進(jìn)行初始化。并且聲明該類處理所有的請(qǐng)求,然后通過這個(gè)類實(shí)現(xiàn)請(qǐng)求轉(zhuǎn)發(fā)。

  • 另外,我們還需要一個(gè)配置文件,用來配置我們需要的相關(guān)的mvc信息。

下面來看一個(gè)完整的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>
1.3.2 注解

注解方式也是現(xiàn)在主流,SpringBoot基于JavaConfig實(shí)現(xiàn)了自動(dòng)配置

實(shí)現(xiàn)方式:

Servlet3.0的時(shí)候定義了一個(gè)規(guī)范SPI規(guī)范。

SPI ,全稱為 Service Provider Interface,是一種服務(wù)發(fā)現(xiàn)機(jī)制。它通過在ClassPath路徑下的META-INF/services文件夾查找文件,自動(dòng)加載文件里所定義的類。也就是在服務(wù)啟動(dòng)的時(shí)候會(huì)Servlet會(huì)自動(dòng)加載該文件定義的類

SpringMVC執(zhí)行流程有哪些

我們看一眼這個(gè)文件里的內(nèi)容。他內(nèi)部定義了SpringServletContainerInitializer容器初始化類,也就是說在Servlet啟動(dòng)的時(shí)候會(huì)自動(dòng)初始化這個(gè)類,這個(gè)類也是注解實(shí)現(xiàn)的關(guān)鍵。

這個(gè)類中存在一個(gè)onStartup方法,這個(gè)也是當(dāng)容器初始化的時(shí)候調(diào)用的方法,這個(gè)方法有兩參數(shù)

  • Set<class<?>&gt; webAppInitializerClasses他代表了當(dāng)前我們的Spring容器中存在的web初始化類。我們自己可以通過實(shí)現(xiàn)WebApplicationInitializer類來自定義Servlet初始化的時(shí)候執(zhí)行的方法。

  • ServletContext servletContex代表了Servlet上下文對(duì)象

org.springframework.web.SpringServletContainerInitializer
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
    @Override
	public void onStartup(Set<class<?>&gt; webAppInitializerClasses, 		
 ServletContext servletContext)    throws ServletException {
        //啟動(dòng)邏輯
    }
}

具體看一下注解配置方式:

public class MyWebApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletCxt) {

        // Load Spring web application configuration
        AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
        //一個(gè)配置類,@Configuration
        ac.register(AppConfig.class);
        //spring的那個(gè)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/*");
    }
}

通過實(shí)現(xiàn)WebApplicationInitializer接口,來作為MVC的配置類,在加載SpringServletContainerInitializer的時(shí)候加載這個(gè)類。


不過在具體的實(shí)現(xiàn)中,Spring不建議我們這樣做,他建議將SpringSpringMvc分開,看個(gè)圖

SpringMVC執(zhí)行流程有哪些

他在Spring之上加了一層Web環(huán)境配置。相當(dāng)于在Spring的外面包裝了一層Servlet

看一下此時(shí)的代碼

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

SpringMVC執(zhí)行流程有哪些

可以看到他實(shí)現(xiàn)了WebApplicationInitializer接口,即在Servlet初始化的時(shí)候會(huì)加載這個(gè)類。

AbstractContextLoaderInitializer類,他初始化了Spring

AbstractDispatcherServletInitializer類,初始化了DispatcherServlet

AbstractAnnotationConfigDispatcherServletInitializer類,將兩個(gè)類整合到一起

2. 實(shí)現(xiàn)原理

聊這個(gè)原理之前,先來聊聊他要干什么?

需求:請(qǐng)求分發(fā);參數(shù)封裝;結(jié)果返回

那如果我們自己來實(shí)現(xiàn),該怎么辦?(單說注解,先來看看我們?cè)趺词褂?code>MVC的)

  • 一個(gè)@Controller注解,標(biāo)識(shí)當(dāng)前類為控制層接口,

  • 一個(gè)RequestMapping標(biāo)識(shí)這個(gè)方法的URI和請(qǐng)求方式等信息

  • 一個(gè)@ResponseBody標(biāo)識(shí)這個(gè)方法的返回類型為JSON

  • 一個(gè)test01標(biāo)識(shí)這個(gè)方法用來處理/test請(qǐng)求

@Controller
public class UserController {

    @GetMapping("/test")
    @ResponseBody
    public String test01(){
        return "success" ;

    }
}

接下來,我們通過我們已有的東西,看一下我們自己去處理請(qǐng)求的邏輯

先來想一下我們的請(qǐng)求過程:

  • 前端發(fā)送一個(gè)Http請(qǐng)求,通過不同的uri實(shí)現(xiàn)不同邏輯的處理

  • 而這個(gè)uri和我們后端的定義的@RequestMapping中的value值相同

  • 即我們可以通過一個(gè)Map結(jié)構(gòu),將value作為key,將methodClass對(duì)象作為一個(gè)value存到一個(gè)MappingRegister

  • 請(qǐng)求來了以后,通過URI從這個(gè)Map中獲取相應(yīng)的Method執(zhí)行,如果沒有對(duì)應(yīng)的Method給一個(gè)404.

2.1 Spring加載

在上面的怎么用中提到了,他通過AbstractContextLoaderInitializer來加載Spring配置文件的。

SpringMVC執(zhí)行流程有哪些

此時(shí)關(guān)于Spring的東西已經(jīng)加載好了,但并未進(jìn)行初始化

2.2 MVC加載

同樣也是通過AbstractDispatcherServletInitializer類實(shí)現(xiàn)

SpringMVC執(zhí)行流程有哪些

2.2.1 DispatcherServlet

接下來我們具體看一下在這個(gè)期間,DispatcherServlet如何處理請(qǐng)求的

作用:分發(fā)所有的請(qǐng)求

類繼承結(jié)構(gòu)圖

SpringMVC執(zhí)行流程有哪些

可以看到他繼承了HttpServlet類,屬于一個(gè)Servlet,而在之前我們配置了這個(gè)Servlet的攔截路徑。他會(huì)將所有的請(qǐng)求攔截,然后做一個(gè)分發(fā)。

下面這個(gè)圖各位看官應(yīng)該非常熟悉:

SpringMVC執(zhí)行流程有哪些

其實(shí)DispatcherServlet處理所有請(qǐng)求的方式在這個(gè)圖里完全都體現(xiàn)了。

接下來聊一下他的設(shè)計(jì)思路吧。

當(dāng)一個(gè)請(qǐng)求來的時(shí)候,進(jìn)入doDispatch方法中,然后處理這個(gè)請(qǐng)求,也是返回一個(gè)執(zhí)行鏈

Spring提供了三種方式的處理器映射器來處理不同的請(qǐng)求。

  • BeanNameUrlHandlerMapping處理單獨(dú)Bean的請(qǐng)求。適用于實(shí)現(xiàn)ControllerHttpRequestHandler接口的類

@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提供的一個(gè)處理通過函數(shù)式編程定義控制器的一個(gè)映射器處理器。需要直接添加到容器中,然后 通過路由一個(gè)地址,返回對(duì)應(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 -&gt; ServerResponse.ok().body("Hello World"))
                .build();
    }

}

聊完了處理器映射器,再來聊一下處理器適配器

不同的請(qǐng)求方式,需要不同的處理方式,這也是Spring為什么要提供一個(gè)適配器的原因。

  • RequestMappingHandlerAdapter用來處理所有的方法請(qǐng)求,即通過@Controller注解定義的

  • HandlerFunctionAdapter用來處理函數(shù)式的映射,即通過RouterFunctionMapping定義的

  • HttpRequestHandlerAdapter用來處理實(shí)現(xiàn)了HttpRequestHandler接口的

  • SimpleControllerHandlerAdapter用來處理實(shí)現(xiàn)了Controller接口的請(qǐng)求

通過處理器適配器拿到適合的處理器,來處理對(duì)應(yīng)的請(qǐng)求。

在處理器執(zhí)行具體的請(qǐng)求的過程,實(shí)際上就是調(diào)用我們的方法的過程,于是就會(huì)出現(xiàn)返回值

通常對(duì)于返回值我們有兩種方法:

  • @ResponseBody直接返回JSON數(shù)據(jù)。

  • 或者返回一個(gè)視圖,該視圖會(huì)被視圖解析器解析。

對(duì)于返回值解析,MVC提供了一個(gè)接口用于處理所有的返回值,這里我們僅僅談上面的兩種

  • ModelAndViewMethodReturnValueHandler用于處理返回視圖模型的請(qǐng)求

  • RequestResponseBodyMethodProcessor用于處理返回JSON

在我們拿到方法返回值以后,會(huì)調(diào)用this.returnValueHandlers.handleReturnValue返回值解析器的這個(gè)方法,用于對(duì)視圖模型的返回和JSON數(shù)據(jù)的回顯(直接回顯到網(wǎng)頁(yè),此時(shí)返回的視圖對(duì)象為null

對(duì)于視圖對(duì)象,通過視圖解析器直接解析,進(jìn)行數(shù)據(jù)模型渲染,然后回顯給前端。

2.2.2 MappingRegistry

這個(gè)類存放了method的映射信息。

class MappingRegistry {

   private final Map<t, mappingregistration<t>&gt; registry = new HashMap&lt;&gt;();

   private final Map<t, handlermethod> mappingLookup = new LinkedHashMap&lt;&gt;();

   private final MultiValueMap<string, t> urlLookup = new LinkedMultiValueMap&lt;&gt;();

   private final Map<string, list<handlermethod>&gt; nameLookup = new ConcurrentHashMap&lt;&gt;();

   private final Map<handlermethod, corsconfiguration> corsLookup = new ConcurrentHashMap&lt;&gt;();

   private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

MVC會(huì)從這個(gè)類中獲取方法和URL的引用。相當(dāng)于Spring MVC的容器。

3. 面試題

3.1 什么是MVC?什么是MVVM?

答:MVC是一個(gè)架構(gòu)模式,它有三個(gè)核心

  • 視圖(View)。用戶界面

  • 模型(Model)。業(yè)務(wù)數(shù)據(jù)

  • 控制器(Controller)。接收用戶輸入,控制模型和視圖進(jìn)行數(shù)據(jù)交互

MVVM也是一種架構(gòu)模式,它也是三個(gè)核心

  • 模型(Model)。后端數(shù)據(jù)

  • 視圖模型(ViewModel)。它完成了數(shù)據(jù)和視圖的綁定

  • 視圖(View)。用戶界面

它的核心思想是:通過ViewModel將數(shù)據(jù)和視圖綁定,用數(shù)據(jù)操作視圖,常見框架為Vue

3.2 Spring Mvc執(zhí)行流程
  • 用戶發(fā)送請(qǐng)求至DispatcherServlet

  • DispatcherServelt收到請(qǐng)求以后調(diào)用HandlerMapping,找到請(qǐng)求處理器映射器(三選一

  • 通過處理器映射器對(duì)應(yīng)URI的處理器執(zhí)行鏈(包含了攔截器,和處理器對(duì)象)

  • 調(diào)用處理器適配器,找到可以處理該執(zhí)行鏈的處理器(四選一)

  • 處理器具體執(zhí)行,返回ModelAndView對(duì)象

    • 如果存在@ResponseBody注解,直接進(jìn)行數(shù)據(jù)回顯

  • 將返回的ModelAndView對(duì)象傳給ViewResove視圖解析器解析,返回視圖

  • DispatcherServlet對(duì)View進(jìn)行渲染視圖

  • 響應(yīng)用戶

關(guān)于SpringMVC執(zhí)行流程有哪些問題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道了解更多相關(guān)知識(shí)。

本文標(biāo)題:SpringMVC執(zhí)行流程有哪些
本文網(wǎng)址:http://bm7419.com/article44/geigee.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供外貿(mào)網(wǎng)站建設(shè)網(wǎng)站建設(shè)、營(yíng)銷型網(wǎng)站建設(shè)軟件開發(fā)、全網(wǎng)營(yíng)銷推廣、面包屑導(dǎo)航

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

營(yíng)銷型網(wǎng)站建設(shè)