本文後續將開啓一個系列,順着做者學習 Spring MVC 文檔的腳步,從零開始搭建一個基於 Spring MVC 的 web 應用,而且根據 Spring MVC 的文檔內容,選擇現有的,用的比較多的,實現性比較好的特性,基於其代碼實現,來說解其源碼和背後的原理,這既是對本身在 Spring 全家桶的學習的檢驗,也可讓我講一講本身對於 Spring MVC 的一些特性的理解。本人也是菜鳥程序員一枚,開始寫這個系列的時候入行也不到半年。所以,儘可能能夠站在初學者的角度,來解決咱們你們在學習的路上一些老師想固然,而咱們卻一直沒有辦法本身解決的問題。但願能夠在不影響工做的狀況下,儘可能作到每週一更甚至每週兩更,但願這個系列能夠幫助到各位。相關代碼能夠移步 個人 github 倉庫,若是有什麼問題,也煩請大佬們在這個系列中,多多指正。php
使用 Intellij 新建工程選項,在 maven 選項卡里面選擇 maven-archetype-webapp,輸入對應的工程名,點擊建立,能夠獲得一個已經初始化了 webapp 文件。html
在 main 文件夾下,分別建立 java 和 resources 文件夾,點擊 File - Project Structure,將 main 和 resources 設置爲 Source Folders (藍色),和 Resource Folder (紫色)。前端
以後,在 java 的文件夾中設置好 package 的路徑,本例的 package 名字爲 com.test.myapp。java
爲了讓工程支持 Spring 的特性,須要在 pom.xml 中引入 springframework 的依賴git
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.0.RELEASE</version>
</dependency>
複製代碼
下面,就開始用代碼實例解釋 Spring MVC 的文檔。程序員
這一部分主要告訴咱們:DispatcherServlet 是 Spring MVC 的中央處理器,它負責把請求分發到控制器中,這被稱爲「前端控制器」的設計模式,模式框架以下圖所示:github
所以,DispatcherServlet 前端的 Incoming Request 發到(Delegate)對應的 Controller 下面,在 Controller 處理了 Request,而且建立 model 以後,將響應和 model 封裝到對應的 view template 中,以後 view template 將控制權交還到 DispatcherServlet,並由其返回響應。web
而實際操做中,DispatcherServlet 因爲繼承了 HttpServlet 類,也須要在 web.xml 文件下進行聲明。所以,能夠照着文檔中的例子進行 DispatchServlet 的配置:spring
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Dispatched Servlet Demo</display-name>
<servlet>
<servlet-name>example</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<!--load on startup 大於 0 表明這個容器在應用啓動時就加載,而等於 0 則表示在這個容器被選擇時加載-->
<!--load-on-startup 的值越小,表明加載的優先級越大-->
</servlet>
<servlet-mapping>
<servlet-name>example</servlet-name>
<url-pattern>/example/*</url-pattern>
</servlet-mapping>
</web-app>
複製代碼
如上述代碼所示,咱們配置了一個名爲 example 的 dispatcherServlet,而當 url 的後綴爲 example 時,默認將使用該 DispatcherServlet 進行請求(request)的轉發和視圖(view )的返回。express
值得一提的是,咱們在 web.xml 中定義了名爲 example 的 DispatcherServlet,Spring 將爲在 WEB-INF 中尋找名爲 example-servlet.xml 的文件以獲取對應的 bean 配置信息。所以,咱們還須要建立 example-servlet.xml 的文件以設置對應的 bean 信息。
example-servlet 中須要咱們指定 bean 的位置,即 Spring MVC 系統須要去哪一個地方尋找 bean 的配置並自動裝配。另外一方面,須要配置 viewResolver 以便返回視圖。所以,咱們在 example-servlet.xml 中首先須要包含這些信息,下述代碼配置了 example-servlet 中的 bean 的掃描路徑,以及視圖解析器 viewResolver。
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<context:component-scan base-package="com.test.myapp.example">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/views/" p:suffix=".jsp" p:order="1">
</bean>
</beans>
複製代碼
首先解釋一下頭文件
頭文件的配置更多的是理解一個相似於 package 的概念,在定義了 namespace(xmlns) 以後,你須要哪些元素或者組件,則定義他的規則以及 instance 的規則,以便後續的 xml 配置使用這些配置。
context 部分表示了這個工程要去哪裏去尋找這個應用的 component,並實現實例的自動裝配,這裏制定了一個針對 component 的過濾器(filter),使它只會在遇到 controller 的類時候進行自動裝配。
在配置好 servlet 的轉發後,咱們須要配置其掃描路徑下的 Controller 和對應的 jsp 視圖。在整理完成對應用途的文件和文件夾以後,工程結構以下圖所示:
![工程文件結構](/Users/shenruofan/Desktop/屏幕快照 2018-10-22 下午5.51.03.png)
首先在 com.test.my.example.controller 下建立對應的 controller 類:
package com.test.myapp.example.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping
public class ExampleController {
@RequestMapping(value="/hello", method = RequestMethod.GET)
public String helloWorld() {
return "hello";
}
}
複製代碼
值得注意的是,因爲在 example-servlet.xml 中已經配置了 url 的格式,它表示這個 servlet 只在 /example/* 的 url 格式下面纔會生效。所以當咱們在 helloWorld 這個方法上指定 RequestMapping 爲 "hello" 時,則令其實際生效的 url 應當爲 /example/hello。因爲 helloWorld() 方法返回了 "hello",則 viewResolver 在 views 下面去尋找名爲 hello.jsp 的文件。所以,咱們還須要一個 hello.jsp 的視圖文件
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>hello world</title>
</head>
<body>
<h1>example::::::hello world!</h1>
</body>
</html>
複製代碼
以後就是運行 tomcat 時所用到的相關配置,而且 maven 打包這個工程,本機使用了 localhost:8080 端口,則輸入了 http://localhost:8080/example/hello 以後,網頁出現了
![效果圖](/Users/shenruofan/Desktop/屏幕快照 2018-10-23 下午3.09.32.png)
的樣子,則說明這個 controller 成功返回了 view 視圖。類比上述「前端控制器」的設計模式,咱們能夠看到:
當前端有請求進來時,好比 /example/hello,Spring MVC 將會識別這個 url 下生效的 servlet,而後該 servlet 會把它分發到 url 映射到的方法上,而該方法會返回一個視圖的名稱到 servlet;以後,servlet 會去找對應名字的 template,組裝完成後,再返回到用戶的前端。所以,咱們才能夠看到 hello.jsp 的前端樣式展示在咱們眼前。
這篇文檔的最後提到了這樣一句話:
當你的應用中只須要一個
DispatcherServlet
時,只配置一個根 context 對象也是可行的。
所以,咱們能夠試一下只配置一個 ContextConfig 的條件下,能不能導航到這個 hello.jsp 文件。
首先,更新 web.xml 的配置,使其只使用一個 ContextConfig 來管理 servlet 下面的 beans,配置內容以下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1">
<display-name>Dispatched Servlet Demo</display-name>
<!--使用根 context 來管理 servlet 的配置-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/root-context.xml</param-value>
</context-param>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
複製代碼
這裏注意一下 servlet-mapping 的 url 攔截問題,其實源文檔的這種配置會有問題,若是不加 .do 的話,會把 jsp 解析的請求也攔截下來,形成 *noHandlerFound for .jsp 的問題,所以須要一個特殊的 url 後綴進行區分,防止 servlet 攔截不應攔截的請求。其次,咱們須要配置 WEB-INF 路徑下的 root-context.xml 文件,配置方法與上面的 example-servlet.xml 的相同。controller 亦然,須要主要的是,如今是當你輸入 /hello.do 時,能夠返回 hello.jsp 的視圖。
以後也是運行 tomcat 時所用到的相關配置,而且 maven 打包這個工程,本機使用了 localhost:8080 端口,則輸入了 http://localhost:8080/hello.do 以後,網頁出現了
![效果圖](/Users/shenruofan/Desktop/屏幕快照 2018-10-23 下午3.09.32.png)
證實咱們的 root context 的配置是有效的。
經過上面的配置,看起來 RootContext 和 servlet 的配置文件對 Spring MVC 工程的做用是同樣的,其實否則,根據官方文檔:
Spring lets you define multiple contexts in a parent-child hierarchy.
The applicationContext.xml defines the beans for the "root webapp context", i.e. the context associated with the webapp.
The spring-servlet.xml (or whatever else you call it) defines the beans for one servlet's app context. There can be many of these in a webapp, one per Spring servlet (e.g. spring1-servlet.xml for servlet spring1, spring2-servlet.xml for servlet spring2).
Beans in spring-servlet.xml can reference beans in applicationContext.xml, but not vice versa.
All Spring MVC controllers must go in the spring-servlet.xml context.
In most simple cases, the applicationContext.xml context is unnecessary. It is generally used to contain beans that are shared between all servlets in a webapp. If you only have one servlet, then there's not really much point, unless you have a specific use for it.
大概的意思是,ApplicationContext.xml 和 Servlet.xml 是父子之間的關係,其中一個 Spring mvc 工程裏應當只有一個 ApplicationContext,然而能夠有多個 Servlet 的配置;而且,Servlet.xml 能夠去引用 ApplicationContext 中的 bean,可是反過來卻不行。
本章討論了 Spring MVC 中 web.xml,以及下面的 servlet.xml 或者 contextConfig.xml 的配置方法及注意事項,並用實際代碼的方式,理解 Spring MVC 的前端控制器設計思想。