SpringMVC基礎

該博客僅爲本人學習時筆記記錄。不能保證沒有錯誤,請結合自己思想參考。

項目源碼:
github地址:https://github.com/JYG0723/springmvcpractice/tree/master

業務流程:

SpringMVC的整體模塊架構:

分析:
1. 由最先的HTPP發送請求,由所配置的XML中的DispatcherServlet處理。

  1. DispatcherServlet接受到這個請求後,根據請求的信息及HandlerMapping的配置找到處理請求的處理器Handler(Controller類)。

  2. 得到HandlerMapping對應的Handler後,通過HandlerAdapter對Handler進行封裝,再以統一的適配器接口調用Handler。

  3. 處理器完成業務邏輯的處理後返回一個ModelAndView給DispatcherServlet。

  4. ModelAndView包含邏輯視圖名,而非真正的視圖對象,DispatcherServlet藉由ViewResolver完成邏輯視圖名到真實視圖對象的解析工作。

  5. 當得到真實View對象後,DispatcherServlet就對這個View對象進行渲染。

  6. 最終返回到客戶的HTTP響應。

前端控制器

Mvc 是開發 Web 應用程序的通用的架構方式

用戶請求頁面後臺實現流程:
1. 首先用戶端的請求通過 http 協議到達了前端控制器。
2. 前端控制器瞭解這個請求應當被誰來處理,所以將這個請求代理給了具體的控制器。
3. 控制器瞭解業務邏輯細節,所以調用業務邏輯生成了業務數據。並且業務數據返回給了前端控制器。
4. 前端控制器再將接受到的數據分發給業務視圖,由業務視圖最終呈現用戶頁面。再將呈現好的用戶頁面返回給前端控制器,並最終將這個頁面返回給瀏覽器端。此時用戶就可以看到了請求頁面。

以醫院看病爲例。前端控制器扮演者 分診臺 的作用,而其他具體 Controller 控制器扮演者各個科室的角色。我們到了醫院分診臺會根據我們具體的病情安排我們到什麼科室去就診

所以可以明白:MVC的核心思想是業務數據抽取同業務數據呈現相分離

Mvc概念

Model + View + Controllerr

View(視圖層):爲用戶提供UI,重點關注數據的呈現
Model(模型層):業務數據的信息表示,關注支撐業務的信息構成。
Controller(控制層):調用業務邏輯產生合適的Model,同時傳遞數據給視圖層用於呈現。

靜態概念

SpringMvc 中前端控制器具體的實現是 DispatcherServlet。用戶請求到達DispatcherServlet 進行分發到達具體的 Controller。

  • HandlerAdapter:HandlerAdapter 是 DispatcherServlet 內部使用的一個類。其實也就是 Controller 的一個表現形式。因爲 DispatcherServlet 不能直接識別每個具體的 Controller 而 HandlerAdapter 卻可以,他用了Java的適配器模式 可以將不同的 Controller 適配成 DispatcherServlet 可以使用的 Handler。這樣 DispatcherServlet 就可以很輕鬆的調用原來的 Controller。

  • HandlerInterceptor:爲我們的Controller加一些料。是一個接口,如果配置了這個類,並提供了實現。就可以在調用 Controller 之前,調用之後,以及 Model 呈現到 View 之後 可以做很多事情。它有三個實現方法

    • preHandle
    • postHandle
    • afterCompletion
  • HandlerMapping:基於 annotation。Handler 是請供求從前段控制器到 Controller 的一箇中間過度類。所以 HandlerMapping 的作用就是告訴 DispatcherServlet 這個請求到達哪個 Controller。同時它也會知道這個Controller包裹了哪些 HaandlerInterceptor 將一個執行鏈條交給 HandlerExecutionChain。

  • HandlerExecutionChain:HandlerMapping返回過來的數據包括,Controller 和 Interceptor 形成的執行鏈條。然後首先去 執行 Interceptor 的 preHandler,然後去調用 Controller 某個業務方法。然後去掉用 postHandlr 最後調用 afterCompletion。

  • ModelAndView:ModelAndView 是 Springmvc 中對 Model 的一種表現形式。Springmvc中 還有其他的類比如 Model,或者 Java 中的 Map 來實現 Model 的功能。Controller 中使用的 Model 或者 Map 這樣的類在 DispatcherServlet 中都會將其轉發成 ModelAndView。所以它可以看成一個 Model 的具體表現

  • ViewResolver:視圖解析器。會告訴 DispatcherServlet 你需要用哪個視圖來進行視圖的呈現,根據配置來找出那一個需要的視圖對象。並將 ModelAndView的數據傳遞給 View。

  • View:負責呈現頁面。

第一個SpringMVC程序

  • 首先需要一個 Servlet 和一個 ServletMapping 將前段控制器配置到web.xml中。

web.xml:

<web-app>
    <display-name>Archetype Created Web Application</display-name>

    <servlet>
        <servlet-name>mvc-dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--DispatcherServlet對應的上下文配置,默認爲/WEB-INF/$servlet-name$-servlet.xml
        -->
        <init-param><!--這裏配置了contextConfigLocation 修改了配置的位置-->
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/configs/spring/mvc-dispatcher-servlet.xml</param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>mvc-dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>
  • 然後根據 web.xml 文件中配置的 DispatcherServlet 對應的上下文配置文件創建 mvc-dispatcher-servlet.xml。
    <context:annotation-config/>

    <context:component-scan base-package="nuc.jyg.controller">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!-- 開啓HandlerMapping -->
    <mvc:annotation-driven/>

    <!-- 配置視圖解析器,告訴DispatcherServlet將用哪個 viewResolver 來獲取view -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/><!-- viewResolver  -->
        <property name="prefix" value="/WEB-INF/jsps/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
  • 最後再創建具體的 Controller 來負責接收用戶端請求。
@Controller
@RequestMapping("/hello")
public class HelloMvcController {

    @RequestMapping("/mvc")
    public String helloMvc() {
        return "home";//home.jsp
    }
}

配置文件解析


+ WebApplicationContext(s):spring配置文件生成的上下文。它由ContextLoaderListener加載生成。他爲所有應用提供了一些公共的組件和服務,Service層Dao層等,這些服務被整個 應用所共享。所以不會侷限在一個DispatcherServlet中。
+ WebApplicationContext:與特定的DispatcherServlet相關的上下文。與其相關的一些有 Controller HandlerMapping ViewResolver等。
+ 可以有多個DispatcherServlet。針對不同的羣體 分發來自這些不同羣體的不同請求。兩個羣體所請求的服務不同,根據每個DispatcherServlet 不同的urlPattern映射路徑來對應具體的DispatcherServlet進行羣體區分並詳細分發。

web.xml:

<?xml version="1.0" encoding="UTF-8"?>

<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"

         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee

    http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <display-name>Archetype Created Web Application</display-name>


    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/configs/spring/applicationContext*.xml</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <servlet>
        <servlet-name>mvc-dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--DispatcherServlet對應的上下文配置,默認爲/WEB-INF/$servlet-name$-servlet.xml
        -->
        <init-param><!--這裏配置了 contextConfigLocation 修改了配置的位置-->
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/configs/spring/mvc-dispatcher-servlet.xml</param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>mvc-dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>
  • 該web.xml文件是maven默認生成的。它運用了一個webapp2.3的標準,這個標準下jsp頁面會默認關閉EL表達式語言。所以如果希望將其打開的話可以使用2.4版本對其替換掉
  • listener:加入了 spring 的聲明
  • context-param:spring 的配置文件的路徑

mvc-dispatcher-servlet.xml:

   <!-- 激活Spring annotation的DI。
    比如@Controller可能會用到一些其他的服務來調用業務邏輯
    ,這裏它需要依賴注入的方式來獲取這些服務,這個標籤的配置就完成了這項功能。-->
    <context:annotation-config/>

    <!-- DispatcherServlet配置上下文。只需要管理@Controller類型的Bean。
    忽略其他類型的bean,如@Service。其他類型的Bean交給Spring上下文管理 -->
    <context:component-scan base-package="nuc.jyg.controller">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>


    <!-- HandlerMapping,無需配置,Spring MVC可以默認啓動。
        DefaultAnnotationHandlerMapping 這個類可以去解析一些基於註解的annotationMapping
        所以不需要對HandlerMapping做一些配置
     -->


    <!--擴充了 註解驅動
    可以將請求參數綁定到控制器參數-->
    <mvc:annotation-driven/>


    <!-- 如果沒有這項配置 將沒有辦法獲得靜態資源
        這裏它將 resource 映射到了本地資源 resource下,該目錄下一般放置了一些css js 圖片等
     -->
    <mvc:resources mapping="/resources/**" location="/WEB-INF/resources/"/>


    <!-- 配置 ViewResolver
            可以用多個 ViewResolver
            可以使用 Order 屬性來進行排序
            InternalResourceViewResolver 需要放在最後 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
        <!-- 給 Controller 中分發出來的jsp的視圖名稱加上 路徑的前綴和後綴
         這樣視圖解析器才能正確的 找到每個視圖並將ModelAndView數據傳輸過去 -->
        <property name="prefix" value="/WEB-INF/jsps/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

層次化的上下文:
spring最外層,然後向內細分。配置springmvc特定的配置文件。
+ context:annotation-config:激活Spring annotation的DI。比如@Controller可能會用到一些其他的服務來調用業務邏輯,這裏它需要依賴注入的方式來獲取這些服務,這個標籤的配置就完成了這項功能。
+ context:component-scan:上面的配置文件中這樣配置,是因爲該配置文件屬於DispatcherServlet配置上下文。只需要管理@Controller類型的Bean。忽略其他類型的bean,如@Service。其他類型的Bean交給Spring上下文管理
+ mvc:annotation-driven:擴展了註解驅動。是註解功能更加豐富。可以將請求參數中的參數直接綁定到映射方法的輸入參數中
+ mvc:resources:如果沒有這項配置 將沒有辦法獲得靜態資源這裏它將 resource 映射到了本地資源 resource下,該目錄下一般放置了一些css js 圖片等
+ bean class=”org.springframework.web.servlet.view.InternalResourceViewResolver:配置 ViewResolver,可以用多個 ViewResolver,可以使用 Order 屬性來進行排序。InternalResourceViewResolver 需要放在最後

applicationContext.xml:

    <!-- 啓動基於annotation的DI管理 -->
    <context:annotation-config/>

    <!-- 關閉對@Controller註解類的管理,因爲已經交由下面的SpringMVC去管理了 -->
    <context:component-scan base-package="nuc.jyg">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
  • context:annotation-config:spring容器中的各式各樣的bean 也會用到其他的服務來調用業務邏輯。也需要通過依賴注入的方式調用這些服務,所以也需要配置這個標籤。
  • context:component-scan:這裏開啓註解掃描標籤。同時排除掉已經在dispatchServlet配置文件中 掃描過的 @Controller 的註解。提升程序性能

Binding

Controller:

@Controller
@RequestMapping("/hello")
public class HelloMvcController {

    @RequestMapping(value = "/mvc", params = "add", method = RequestMethod.GET)
    public String helloMvc() {
        return "/course/home2";
    }
}
  • params:請求的參數限定。即請求中必須攜帶該請求參數
  • return “/course/home2”:返回的jsp視圖的映射路徑。根據dispatchServlet配置文件的 資源文件的映射路徑的配置 以及此處的詳細映射路徑的指定 可以找到具體的視圖。

Controller:

 //@RequestMapping(value = "/mvc/{id}",method = RequestMethod.GET)
 @RequestMapping(value = "/mvc", params = "username", method = RequestMethod.GET)
    public String helloMvc(HttpServletRequest httpServletRequest, Model model) {
        logger.info("param1" + httpServletRequest.getRequestURI() + "|| param2" + httpServletRequest.getRequestURL());
        logger.info(httpServletRequest.getParameter("username"));
        logger.info(httpServletRequest.getContextPath());
        model.addAttribute(httpServletRequest);
        return "/course/home2";
    }

    @RequestMapping(value = "/save", method = RequestMethod.POST)
    public String doSave(User user) {
        //如果這裏的映射方法中需要接受表單中完整 User模型對象的數據。可以直接在參數中加上一個User對象。
        // 這樣就必須保證前臺表單中的input標籤的name屬性的值要和User對象中的屬性名一致。這樣就完成了數據綁定
        //如果這裏不是user對象,而是String username,String password。也一樣,要想實現數據綁定前臺input標籤中的name屬性也要是username和password
        user.setUsername("li4");
        return "redirect:mvc?username=" + user.getUsername();
        return "redirect:mvc/"+user.getId;
    }
  • return “redirect:當插入課程成功之後,需要對插入課程的詳細信息進行展示。在dosave方法對前臺傳過來的課程數據持久化之後就可以重定向到相應的展示方法進行插入的課程的詳細信息的展示。
  • logger.info(ReflectionToStringBuilder.toString(user)):這個方法他會將接收到的參數內容以鍵值對的形式輸出到日誌中。
  • doSave方法中的User參數:如果這裏的映射方法中需要接受表單中完整 User模型對象的數據。可以直接在參數中加上一個User對象。這樣就必須保證前臺表單中的input標籤的id屬性的值要和User對象中的屬性名一致。這樣就完成了數據綁定

@ModelAttribute

在Spring MVC裏,@ModelAttribute通常使用在Controller方法的參數註解中,用於解釋model entity,但同時,也可以放在方法註解裏。

如果把@ModelAttribute放在方法的註解上時,代表的是:該Controller的所有方法在調用前,先執行此@ModelAttribute方法。

比如我們有一個Controller:TestController:

@Controller
@RequestMapping(value="test")
public class PassportController {

    @ModelAttribute
    public void preRun() {
        System.out.println("Test Pre-Run");
    }

    @RequestMapping(method=RequestMethod.GET)
    public String index() {
        return "login/index";
    }

    @RequestMapping(value="login", method=RequestMethod.POST)
    public ModelAndView login(@ModelAttribute @Valid Account account, BindingResult result)
        :
        :
    }

    @RequestMapping(value="logout", method=RequestMethod.GET)
    public String logout() {
        :
        :
    }

}

在調用所有方法之前,都會先執行preRun()方法。

我們可以把這個@ModelAttribute特性,應用在BaseController當中,所有的Controller繼承BaseController,即可實現在調用Controller時,先執行@ModelAttribute方法。

比如權限的驗證(也可以使用Interceptor)等

單文件上傳

首先需要在DiapatherServlet包中配置這樣一個bean。

<bean id="" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="209715200"/>
        <property name="defaultEncoding" value="UTF-8"/>
        <property name="resolveLazily" value="true"/>
</bean>

這個bean專門用來解析上載的文件。

然後再在pom.xml中引入這個類依賴的commons-fileupload包。

jsp:

<form method="post" action="/hello/mvc" enctype="multipart/form-data">
    <input type="file" name="file">
</form>

文件上傳時用到的form表單中必須有enctype這個屬性。並且指定其屬性值爲 multipart/form-data。表示該表單是要處理的

Controller:

@RequestMapping(value = "/doUpload", method = RequestMethod.POST)
    public String doUploadFile(@RequestParam("file") MultipartFile multipartFile) throws IOException {
        if (!multipartFile.isEmpty()) {
            logger.info("11111", multipartFile.getOriginalFilename());
            FileUtils.copyInputStreamToFile(multipartFile.getInputStream(), new File("E:\\test\\", System.currentTimeMillis() + multipartFile.getOriginalFilename()));
        }
        return "course/success";
    }

對象的輸入輸出流的作用: 用於寫入對象 的信息和讀取對象的信息。 使得對象持久化。

Controller 中對應的映射方法就可以通過 MultipartFile 這個接口來接收傳遞來的文件對象。並對上傳來的文件對象進行操作。
FileUtils:這個類可以對文件進行拷貝,以及對文件流操作。

多文件上傳

多文件上傳其實很簡單,和上傳其他相同的參數如 checkbox 一樣,表單中的每一個 input的file元素 使用相同的名稱,然後 action 中將MultipartFile參數類定義爲數組就可以。

jsp:

<body>  
    <h2>上傳多個文件 實例</h2>  


    <form action="filesUpload.html" method="post"  
        enctype="multipart/form-data">  
        <p>  
            選擇文件:<input type="file" name="files">  
        <p>  
            選擇文件:<input type="file" name="files">  
        <p>  
            選擇文件:<input type="file" name="files">  
        <p>  
            <input type="submit" value="提交">  
    </form>  
</body>  

Controller:

/*** 

     // 保存文件的邏輯,單獨抽出一個來做一個方法。方便單文件上傳和多文件上傳不同的調用
    private boolean saveFile(MultipartFile file) {  
        // 判斷文件是否爲空  
        if (!file.isEmpty()) {  
            try {  
                // 文件保存路徑  
                String filePath = request.getSession().getServletContext().getRealPath("/") + "upload/"  
                        + file.getOriginalFilename();  
                // 轉存文件  
                file.transferTo(new File(filePath));  
                return true;  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
        }  
        return false;  
    }  

    @RequestMapping("filesUpload")  
    public String filesUpload(@RequestParam("files") MultipartFile[] files) {  
        //判斷file數組不能爲空並且長度大於0  
        if(files!=null&&files.length>0){  
            //循環獲取file數組中得文件  
            for(int i = 0;i<files.length;i++){  
                MultipartFile file = files[i];  
                //保存文件  
                saveFile(file);  
            }  
        }  
        // 重定向  
        return "redirect:/list.html";  
    }  

mvn jetty:run

cmd 中利用maven中你配置的項目部署插件。注意要切換到項目根目錄下啓動cmd來運行 mvn jetty:run 啓動項目

  <build>
        <plugins>
            <plugin>
                <!-- https://mvnrepository.com/artifact/org.eclipse.jetty/jetty-maven-plugin -->
                <groupId>org.eclipse.jetty</groupId>
                <artifactId>jetty-maven-plugin</artifactId>
                <version>9.4.3.v20170317</version>
            </plugin>
        </plugins>
    </build>

Json

SpringMVC中默認開啓對json的支持

ViewResolver:可以將相同的數據呈現成不同的數據表現形式。

SpringMVC有自己默認的數據表現形式。你也可以在dispatcherServlet.xml文件中進行覆蓋,選用自己配置的ViewResolver。

數據形式的多種形式表述(html/xml/json/pdf/excel),spring有兩種表述形式:

org.springframework.web.servlet.view.ContentNegotiatingViewResolver
              ・ org.springframework.http.converter.HttpMessageConverte

可以根據自己的需要選擇表述形式。或對選擇好的表述形式的屬性進行自定義覆蓋配置。其都含有自己默認的配置

@PathVariable:當該註解沒有指定當前參數對應綁定的路徑中的請求參數的名稱的時候。默認根據參數名去路徑變量中尋找對應參數進行匹配

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章