天天看點

小白新手web開發簡單總結(十六)-Web層優化(DispatcherServlet源碼分析)前言一 DispatchServlet二 執行個體 三 總結

目錄

前言

一 DispatchServlet

1.什麼是DispatchServlet

2.源碼分析

(1)幾個重要的屬性

(2)onRefresh

(2)關鍵方法doService()

3.總結DispatchServlet

二 執行個體

1.web.xml中配置DispatchServlet

2.配置Spring MVC的context的内容

3.使用@Controller和 @RequestMapping增加業務邏輯代碼

 三 總結

前言

經過小白新手web開發簡單總結(十一)-資料庫連接配接的相關優化(資料源DataSource)、小白新手web開發簡單總結(十二)-資料庫連接配接的相關優化(事務管理)、小白新手web開發簡單總結(十三)-資料庫連接配接的相關優化( Hibernate的使用)、小白新手web開發簡單總結(十五)-資料庫連接配接的相關優化( MyBatis的使用)已經将小白新手web開發簡單總結(八)-資料庫HSQLDB執行個體這個中的資料通路層(DAO層)的邏輯已經優化好了,增加了資料庫連接配接池、ORM以及業務邏輯層增加事務管理。但是在小白新手web開發簡單總結(八)-資料庫HSQLDB執行個體中的web層一直都是直接通過HttpServlet來處理浏覽器發送過來的請求。而在Spring MVC中有一個專門前端控制器DispatchServlet,進行URL的請求處理。

web層的Spring MVC架構具有開發靈活,松散耦合特點:

  • Model層:封裝了需要傳回給浏覽器顯示的資訊,一般是HTML;
  • View層:網頁、jsp,展示Model層的資料;
  • Controller層:處理浏覽器發送過來的請求,将不同的Model,傳遞到對應的View中

一 DispatchServlet

1.什麼是DispatchServlet

DispatchServlet實際上繼承于HttpServlet,是一個标準的Servlet。作用:根據設定的比對規則進行攔截浏覽器發出的請求,然後将請求轉發給Spring MVC控制器。

2.源碼分析

(1)幾個重要的屬性

  • private MultipartResolver multipartResolver:用于處理檔案上傳服務,如果有檔案需要上傳,那麼就需要将目前的HttpServletRequest包裝成DefaultMultipartHttpServletRequest,并将麼個上傳的内容封裝成CommonsMultipartFile對象
  • private LocaleResolver localeResolver:國際化問題,本地解析政策
  • private ThemeResolver themeResolver:解析主題,比如可以定制個性化版面
  • private List<HandlerMapping> handlerMappings:主要負責預處理、後處理等一系列執行處理和滿足條件的控制器的執行,如請求映射關系
  • private List<HandlerAdapter> handlerAdapters:根據Handler的類型定義不同的處理規則
  • private List<HandlerExceptionResolver> handlerExceptionResolvers:當Hander處理出錯後,會通過該類将錯誤日志儲存在log檔案中,預設的實作類為SimpleMappingExceptionResolver
  • private RequestToViewNameTranslator viewNameTranslator:将指定的viewName按照定義的RequestToViewNameTranslator替換成想要的格式
  • private FlashMapManager flashMapManager:用于生成FlashMap管理器
  • private List<ViewResolver> viewResolvers:根據視圖名稱解析視圖。

(2)onRefresh

protected void onRefresh(ApplicationContext context) {
        this.initStrategies(context);
    }
           

 通過該方法來初始化裡面的各個屬性。基本邏輯就是首先會讀取在自行配置的bean,如果沒有配置,則讀取預設的 org.springframework.web.servlet目錄下的DispatchServlet.properties裡面指定的bean:

# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
	org.springframework.web.servlet.function.support.RouterFunctionMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
	org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
	org.springframework.web.servlet.function.support.HandlerFunctionAdapter


org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
	org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
	org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
           

(2)關鍵方法doService()

因為DispatchServlet繼承于HttpServlet,那麼當接收到對應比對的url之後,那麼就會執行到doService()方法,那麼其中關鍵的實作應該就在該doService()中。梳理doSerivice()代碼,裡面重要的代碼邏輯就是調用了

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
  //。。。。。。。
        //最終就是調用了下面的方法來實作邏輯
        this.doDispatch(request, response);
  //。。。。。。。
}
           

進入到doDispatch()中看下具體的邏輯:源碼可以具體檢視在 org.springframework.web.servlet目錄下的DispatchServlet.java代碼,這裡主要辨別幾個主要的邏輯:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            try {
                ModelAndView mv = null;
                Object dispatchException = null;

                try {
                    //1.如果指定multipartResolver,則将request包裝成MultipartHttpServletRequest
                    processedRequest = this.checkMultipart(request);
                    //2.擷取該請求對應的HandlerMapper
                    mappedHandler = this.getHandler(processedRequest);
                    //3.擷取該Handler對應的HandlerAdapter
                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
               //=== HandlerMapper的攔截作用,調用攔截器HandlerInterceptor的preHandler()
                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }
                    //4.執行Handler裡面的業務邏輯,傳回ModelAndView
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    //5.如果沒有設定視圖,則應用預設的視圖
                    this.applyDefaultViewName(processedRequest, mv);
              //=== HandlerMapper的攔截作用,調用攔截器HandlerInterceptor的postHandle()
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                } catch (Exception var20) {
                } 
                //6.将封裝的ModelAndView轉發到對應的頁面
              //=== HandlerMapper的攔截作用,調用攔截器HandlerInterceptor的afterCompletion()
                this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
            } catch (Exception var22) {    
            } 
        } finally {
        }
    }
           
  • 第一步:如果指定multipartResolver,則通過checkMultipart(request)将request包裝成MultipartHttpServletRequest,供後面去使用
  • 第二步:通過getHandler(processedRequest)擷取MapperHandler

每個請求都需要一個HandlerMethod來處理,具體接收一個請求交給哪個HandlerMethod來處理,那就是有HandlerMapping來處理。

HandlerMethod(控制器)也就是我們在項目中定義實作業務邏輯的地方,有多種實作方式:
  • 1)實作org.springframework.web.servlet.mvc.Controller接口
通過複寫handleRequest()來實作具體的業務邏輯
  • 2)基于@Controller注解方式

          @Controller:可以傳回到指定的如jsp、ftl、html等模版頁面,需要配合視圖解析器InternalResourceViewResolver才可以,若要傳回實體對象,必須在增加@@ResponseBody

          @RestController:相當于@[email protected],隻能傳回String、Object、Json等實體對象,無法傳回jsp或者http頁面

  • 3)實作org.springframework.web.HttpRequestHandler接口
通過複寫handleRequest()來實作具體的業務邏輯

HandlerMapping就是負責将url映射到對應的handler上,根據請求的url查找對應的實際業務邏輯處理的Handler,具體完成兩項工作:

  •  1)根據請求擷取HandlerMethod,即目前的請求具體交給哪個Controller的哪個方法來處理;
  •  2)将HanlderMethod和目前系統的Interceptor又被封裝到HanlderExcutionChain,最後傳回的就是這個HanlderExcutionChain對象,完成Interceptor的攔截作用。
HandlerMapping(url mapping的控制器),有幾種實作方式:
  • 1)org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping:預設的方式,不需要顯示聲明。需要在配置檔案中将url和controller name綁定到一起
  • 2)org.springframework.web.servlet.handler.SimpleUrlHandlerMapping:通過properties形式,配置url和controller的映射,而不需要controller name,需要顯示聲明SimpleUrlHandlerMapping
  • 3)org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping:通過@RequestMapping,配置url和controller的映射
  • 4)org.springframework.web.servlet.function.support.RouterFunctionMapping 
  • 第三步:通過getHandlerAdapter(mappedHandler.getHandler())擷取HandlerAdapter

周遊所有的HandlerAdapter集合,查找支援到這次請求的HandlerAdapter。

HandlerMethod要與HandlerAdapter配合使用,有什麼樣的Handler就要配置什麼樣的HandlerAdapter:
  • 1)org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter:Http請求處理擴充卡:處理的HandlerMethod是需要實作HttpRequestHandler接口的實作類
  • 2)org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter:簡單控制器處理擴充卡:處理的HandlerMethod是需要實作Controller的實作類
  • 3)org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter:通過@Controller的處理擴充卡:處理的HandlerMethod是需要增加@Controller的類
  • 4)org.springframework.web.servlet.function.support.HandlerFunctionAdapter
  • 第四步:通過 ha.handle(processedRequest, response, mappedHandler.getHandler())擷取ModelAndView

在Handler裡會含有一些邏輯處理,邏輯處理完會産生一些需要顯示到浏覽器中的資訊,這些資訊稱為Model,當然這些Model是普通使用者無法檢視的,是以就需要将這些Model發送到一個視圖View(如jsp),這個Model和View封裝到一起就是ModelAndView。

在找到對應的HandlerAdapter之後,那麼在HandlerAdapter中就根據特定規則,通過handle()去執行Handler(也就是在項目中增加的url對應的業務邏輯的方法裡面的功能,通常也會在該項目中對應的業務邏輯方法中傳回ModelAndView對象),最後将Model和View打包,傳回ModelAndView。那麼我覺得這個HandlerAdapter也就是承擔了在MVC中的Controller的作用:處理浏覽器發送過來的請求,并将傳回的Model傳遞給View層,這樣控制器和特定的視圖進行解耦。

ModelAndView:模型和視圖(View)。視圖的設定方式有兩種:可采用字元串視圖名稱形式,通過配置的ViewResolver對象解析成View;或者直接設定View。
  • 1)可以通過不同的構造函數指定傳回的頁面,如
public ModelAndView(String viewName) {
        this.view = viewName;
    }
           
  • 2)可以通過setViewName()跳轉到指定的頁面,如
public void setViewName(@Nullable String viewName) {
        this.view = viewName;
    }
           
  • 3)可以通過addObject()方法傳回給View(如jsp)相應的資料,在View中可以直接通過attributeName取出對應的值。
public ModelAndView addObject(String attributeName, @Nullable Object attributeValue) {
        this.getModelMap().addAttribute(attributeName, attributeValue);
        return this;
  }
           

預設的ViewResolver為org.springframework.web.servlet.view.InternalResourceViewResolver。

InternalResourceViewResolver用于通路如jsp、HTML、XHTML這樣的View,其中有prefix(字首)和suffix(字尾)兩個屬性,将在ModelAndView中配置的視圖字元串轉換成真實路徑的資源url。prefix配置的就是View存放的路徑,而suffix配置的就是View的字尾名

前面四步都是Controller層的相關邏輯,接下來就是Model層和View層 

  • 第五步:如果ModelAndView沒有設定view,則通過applyDefaultViewName(processedRequest, mv)給定一個預設的View

遺留問題:需要驗證下是不是該第四步傳回的ModelAndView是不是就是Handler中傳回的ModelAndView呢?

解答問題:這個就是我們在Controller中定義的與url相關的Handler裡面執行個體化的ModelAndView

遺留問題:需要驗證下該ModelAndView預設的View是指的什麼?

解答問題:例如jsp就會對應的JspView等,就是實際的渲染的View

  • 第六步:通過processDispatchResult()将最後的ModelAndView的Model轉發到對應到View中。

該方法的主要功能展現在this.render(mv, request, response)中,進入到該方法中看下主要邏輯:

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
        String viewName = mv.getViewName();
        View view;
        if (viewName != null) {
        //1.如果擷取ModelAndView中設定了viewName,則通過配置的viewResolvers将字元串路徑轉換成View
            view = this.resolveViewName(viewName, mv.getModelInternal(), locale, request);
        } else {
         // 2.如果沒有設定viewName,則從ModelAndView中擷取View
            view = mv.getView();
        }
        try {
            if (mv.getStatus() != null) {
                response.setStatus(mv.getStatus().value());
            }
           //3.将Model傳遞到view
            view.render(mv.getModelInternal(), request, response);
        } catch (Exception var8) {
        }
    }
           

 這樣就完成了将Model層的資訊顯示到View層。

通過上面六步之後完成了一個請求從接收到傳回給浏覽器的整個過程,當然剛才在介紹HandlerMapping作用的時候,一個作用就是url和controller的映射,另外一個作用就是Interceptor攔截作用,那麼其實在可以看到在doDispatch()方法中的第四步執行業務邏輯之前、第六步業務邏輯之後,将Model傳回給View之前、以及最後完成View渲染(這個相關代碼在render()中實作的)之後已經分别完成Interceptor對應的方法的調用。

小白新手web開發簡單總結(十六)-Web層優化(DispatcherServlet源碼分析)前言一 DispatchServlet二 執行個體 三 總結

那麼很明顯的Interceptor主要作用攔截Controller請求,作用在使用者的每一次請求過程中,那麼像一些使用者登陸權限的校驗等就可以放到Interceptor中,對一些靜态資源無法攔截。一般需要通過實作HandlerInterceptor或繼承HandlerInterceptorAdapter,通常配置在該Servlet的配置檔案中;

public interface HandlerInterceptor {
    //在業務處理器處理請求之前被調用。預處理,可以登陸校驗、權限校驗等
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }
    //在業務處理器處理請求執行完成後,生成視圖之前
    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }
    //完全處理請求後被調用,可用于清理資源等。處理傳回,此時已經完成了頁面渲染
    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    }
}
           

而在web.xml配置的Filter主要作用統一設定字元集等。依賴于Servlet容器,僅在初始化的時候調用一次。一般需要實作Filter接口,通常配置在web.xml中。

public interface Filter {
    //filter對象建立時執行(Tomcat加載Filter時)
    default void init(FilterConfig filterConfig) throws ServletException {
    }
    //執行過濾的邏輯的具體實作
    void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
    //filter對象銷毀時(Tomcat關閉時)
    default void destroy() {
    }
}
           

3.總結DispatchServlet

經過源碼分析之後,再來看其DispatchServlet整個過程大體如下:

(1)DispatchServlet通過比對到對應的url,攔截對應請求;

(2)通過HandlerMapping來比對到對應的Handler,同時傳回增加了攔截功能的HandlerExecutionChain;

(3)通過HandlerMapping中的對應的Handler找到對應的HandlerAdapter,通過HandlerAdapter來執行Handler的相關業務邏輯;

(4)在HandlerAdapter 中将Handler傳回的Model以及View的封裝ModelAndView傳回給DispatchServelet;

(5)DispatchServelet調用ViewResolver傳回View;

(6)完成View的渲染。

遺留問題:需要增加一個流程圖來進一步分析DispatchServlet

小白新手web開發簡單總結(十六)-Web層優化(DispatcherServlet源碼分析)前言一 DispatchServlet二 執行個體 三 總結

二 執行個體

還是通過一個執行個體來看下Spring MVC到底是怎麼優化小白新手web開發簡單總結(八)-資料庫HSQLDB執行個體中的第(6)步的操作。

1.web.xml中配置DispatchServlet

<!--增加Spring MVC-->
    <servlet>
        <servlet-name>sql-web</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--如果需要設定init-param,必須要放到load-on-startup之前-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:config/spring-context.xml</param-value>
        </init-param>
        <!--表示啟動容器的時候就初始化該Servlet-->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>sql-web</servlet-name>
        <!--https://blog.csdn.net/weixin_44415997/article/details/100098081解決路徑比對不到的情況-->
<!--        <url-pattern>/mvc/*</url-pattern>-->
        <!--第二種解決方式:直接通過添加字尾名的形式來攔截請求。該字尾名任意-->
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>
           
  • (1)<servlet-mapping>

每個<servlet>必須比對<servlet-mapping>,通過<servlet-mapping>去攔截比對<url-pattern>規則的相應的浏覽器發送過來的請求:如果比對成功,那麼對應的這些請求就交給DispatcherServlet來處理。

具體對這個的一個了解可參見小白新手web開發簡單總結(十七)-DispatcherServlet中的url-pattern的一點反思。在本執行個體中采用字尾名的方式來進行攔截url。

<!--第二種解決方式:直接通過添加字尾名的形式來攔截請求。該字尾名任意-->
        <url-pattern>*.html</url-pattern>
           

 其中像<serlvet-name>等這些簡單的配置項不在多餘介紹,主要總結下下面兩個:

  • (2)<init-param>

其中DispatcherServet使用的預設的WebApplicationContext作為上下文,預設的配置讀取的配置檔案是“/WEB-INF/<servlet-name>-servlet.xml”,當然DispatcherServet也提供了<init-param>來配置初始化參數,進而替換預設值。

  • contextConfigLocation

指定Context的位置,這個<param-value>可以指定多個字元串,使用“,”作為分隔符。

<init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:config/application-context*.xml</param-value>
        </init-param>
           
  • contextClass

實作了WebApplicationContext的類,預設的就是org.springframework.web.context.support.XmlWebApplicationContext。該類用來建立web元件的JavaBean,如控制器、視圖解析器以及處理器映射。

<init-param>
            <param-name>contextClass</param-name>
            <param-value></param-value>
        </init-param>
           

這個WebApplicationContext與小白新手web開發簡單總結(九)-ContextLoaderListener通過ContextLoaderListener中傳回的WebApplicationContext是由不同作用的,可以看見小白新手web開發簡單總結(九)-ContextLoaderListener。

ContextLoaderListener初始化的WebApplicationContext是為整個應用程式所共享,不管表現層采用什麼架構,主要針對的就是Dao層、Service層的JavaBean;

而通過DispatcherServlet初始化的WebApplicationContext隻是針對Spring Web MVC有效的JavaBean,如Controller、HandlerMappping、HandlerAdapter等等,主要針對的web相關的JavaBean。

并且DispatcherServlet初始化的WebApplicationContext會将ContextLoaderListener初始化的WebApplicationContext設定為Parent。

遺留問題:感覺還是沒有了解透徹,需要在細一點,為什麼要有這兩個WebApplicationContext呢?兩個可以互相使用嗎(加載對方的xml檔案)?

注意如果要設定<init-param>一定要放到 <load-on-startup>之前。

  • (3)<load-on-startup>

用來标記web應用啟動的時候,加載對應Servlet的順序:

(1)如果>=0:在web應用啟動的時候,就加載該Servlet。數字越小,優先級越高;當值一緻的時候,自行選擇加載。

(2)當不設定該配置項或者<0時:在使用Servlet的時候,在加載該Servlet。

2.配置Spring MVC的context的内容

在DispatcherSerlvet的源碼中可以看到裡面有一個預設的org/springframework/web/servlet/DispatcherServlet.properties檔案,當然這些屬性也可以自行定義。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:p="http://www.springframework.org/schema/p"
       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:annotation-driven/>-->
    <!--    <mvc:default-servlet-handler/>-->
    <!--如果要使用RequestMappingHandlerMapping這種方式,必須增加,否則無法加載對應注解的類-->
    <context:component-scan base-package="com.wj.mysql.controller"/>
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>

    <!--    <bean id=""-->
    <!--在web.xml中也可以配置攔截器-->
    <!--    <mvc:interceptors>-->
    <!--        <mvc:interceptor>-->
    <!--            <mvc:mapping path="/**"/>-->
    <!--            <ref bean=""-->
    <!--        </mvc:interceptor>-->
    <!--    </mvc:interceptors>-->
    <!--jsp最佳是放到WEB-INF檔案下,避免JSP可以通過手動輸入的url被直接通路到,隻有控制器才能通路。
    支援多個view resolver。-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          p:suffix=".jsp"
          p:prefix="/WEB-INF/views"/>
    <!--解決路徑模糊比對,頁面404-->
    <!--  <bean class="com.wj.mysql.utils.SetAlwaysUseFullPathForDispatcher"/>-->

</beans>
           

這裡唯一要提的一點就是如果要使用RequestMappingHandlerMapping這種方式,必須增加  <context:component-scan base-package="com.wj.mysql.controller"/>,否則無法加載對應注解的類。base-package就是你對應的所有Spring MVC對應的類的包。

可以配置攔截器,用來對該次請求進行攔截。

可以增加InternalResourceViewResolver,通過添加p:prefix和p:suffix,這樣在跳轉到對應的jsp頁面的時候,就不需要添加對應的路徑,隻需要使用jsp的檔案名就可以了。

3.使用@Controller和 @RequestMapping增加業務邏輯代碼

代碼邏輯很簡單就是利用之前增加的DAO層的代碼,完成一次插入和查詢操作,并且把查出的内容放到booklist.jsp中顯示

@Controller
@RequestMapping("/mvc")
public class BookMvcController {
    @Autowired
    private BookServiceImpl service;
    @RequestMapping(value = "/getbook")
    public ModelAndView getBook(HttpServletRequest request,
                                HttpServletResponse response) {
        Book book = new Book();
        book.setName("Spring MVC");
        book.setPrice(49.0);
        book.setOnline(new Date());
        int result = service.insert(book);
        System.out.println("=====  插入的資料,傳回為:" + result);
        List<Book> books = service.select();
        System.out.println("====   查詢的資料為:");
        for (Book bo : books) {
            System.out.println(bo.toString());
        }
        ModelAndView modelAndView = new ModelAndView("/booklist");
        modelAndView.addObject("book",books);

        //增加業務處理之後,将傳回的資料傳遞給ModelAndView
        return  modelAndView;
    }
}
           

對應的booklist.jsp的代碼邏輯如下

<%@ page import="com.wj.mysql.model.Book" %>
<%@ page import="java.util.List" %><%--
  Created by IntelliJ IDEA.
  User: wenjing.liu
  Date: 2021/3/31
  Time: 14:18
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<P>目前資料庫中的所有的書的資訊如下:</P>
<%
    List<Book> bookList = (List<Book>) request.getAttribute("book");
    for (int i = 0; i < bookList.size(); i++) {
        Book book = bookList.get(i);
%>
        <label><%= book.toString() %>
        </label>
        <br>
<%
    }
%>

</body>
</html>
           

通過IDEA成功運作項目,通過通路http://localhost:8080/mvc/getbook.html,已經可以成功的顯示目前資料庫中已有的資料的資訊。 

代碼以及上傳到github:https://github.com/wenjing-bonnie/sql-web.git對應的代碼的tag為example16(因為項目在一直更新,是以通過打tag的方式來标記這次代碼) 

通過@Controller和 @RequestMapping兩個注解,簡化之前方式自行去繼承HttpServlet的方式。在寫請求的時候,可以不在需要對每個請求單獨去繼承HttpServlet。

 三 總結

  • 1.DispatcherServlet繼承于HttpServlet,可根據設定的url比對原則,攔截浏覽器發出的請求;
  • 2.在DispatcherServlet中有幾個重要的屬性,預設值在org.springframework.web.servlet目錄下的DispatchServlet.properties目錄下,如果在項目中不指定,則使用預設值
  • 3.HandlerMethod對應項目中url比對的處理業務邏輯的方法;
  • 4.HandlerMapping是url與HandlerMethod的映射關系的集合,常用的為RequestMappingHandlerMapping,即通過@RequestMapping的方式将url與HandlerMethod進行映射;
  • 5.HandlerMapping還具有Interceptor作用,可以針對該次的請求進行攔截處理;
  • 6.HandlerAadapter是根據HandlerMethod來找到對應的處理擴充卡,用來執行HandlerMethod中的處理業務的方法,與RequestMappingHandlerMapping對應的處理擴充卡為RequestMappingHandlerAdapter;
  • 7.通過HandlerAadapter執行完業務邏輯之後,傳回ModelAndView;是以HandlerAadapter承擔了MVC模型中的Controller的作用;
  • 8.根據設定的ViewResolver,将對應業務邏輯處理之後的資訊Model,傳遞給View,完成View的渲染;

經過前面的一些積累,在使用web應用開發中的一些架構,顯得比以前輕松很多。

繼續閱讀