主要參考了 [IMOOC-SpringMVC 起步](http://www.imooc.com/video/7237) 視頻教程和 [SpringMVC從入門到精通 系列 - HansonQ](http://www.imooc.com/article/3804) ,還有自己的一些總結。MVC 簡介、... ...
MVC 簡介
1、MVC 是一種架構模式
程式分層,分工合作,既相互獨立,又協同工作,分為三層:模型層、視圖層和控制層
2、MVC 是一種思考方式
View:視圖層,為用戶提供UI,重點關註數據的呈現,為用戶提供界面
Model:模型層,業務數據的信息表示,關註支撐業務的信息構成,通常是多個業務實體的組合
Controller:控制層,調用業務邏輯產生合適的數據(Model),傳遞數據給視圖用於呈現
MVC 設計模式在 B/S 下的應用:
①:瀏覽器發送請求到控制器(這裡要知道控制器的作用)
②:控制器不能處理請求必須交給模型層來處理接著去訪問資料庫
③:模型層將處理好的結果返回給控制層
④:控制層將邏輯視圖響應給瀏覽器(瀏覽器顯示的是渲染過的視圖)
MVC 本質:MVC 的核心思想是業務數據抽取同業務數據呈現相分離;分離有利於程式簡化,方便編程
前端控制器模式
前端控制器模式(Front Controller Pattern)是用來提供一個集中的請求處理機制,所有的請求都將由一個單一的處理程式處理。該處理程式可以做認證/授權/記錄日誌,或者跟蹤請求,然後把請求傳給相應的處理程式。
- 前端控制器(Front Controller)- 處理應用程式所有類型請求的單個處理程式,應用程式可以是基於 web 的應用程式,也可以是基於桌面的應用程式。
- 調度器(Dispatcher) - 前端控制器可能使用一個調度器對象來調度請求到相應的具體處理程式。
- 視圖(View) - 視圖是為請求而創建的對象。
前端控制器的主要作用:
- 指前端控制器將我們的請求分發給我們的控制器去生成業務數據
- 將生成的業務數據分發給恰當的視圖模版來生成最終的視圖界面
SpringMVC 基本概念
對組件說明:
1、DispatherServlet:前端控制器 用戶請求到達前端控制器,相當於 MVC 中的 C,而 DispatherServlet 是整個流程的核心,它來調用其他組件來處理用戶的請求,前端控制器的存在降低了其他組件之間的耦合度。
2、HandlerMapping:處理器映射器 它的作用就好比去看電影要拿著電影票根據電影票上面的座位號找到座位其中座位就是 Handler,電影票以及上面的座位號就是 URL HandlerMapping 負責根據用戶請求找到 Handler 即處理器,SpringMVC 提供了不同的映射器實現不同的映射方式,例如:配置文件方式,實現介面方式,註解方式等。
3、Handler:處理器 Handler 是後端控制器,在前端控制器的控制下後端控制器對具體的用戶請求進行處理,Handler 涉及到具體的用戶業務請求,所以一般情況下需要程式員根據業務需求開發。
4、HandlerAdapter:處理器適配器 通過 HandlerAdapter 對處理器進行執行,這是適配器模式的應用,通過適配器可以對更多類型的處理器進行執行。播放的電影是 3D 的你看不清楚,因此電影院跟你說你要想看清電影就必須戴 3D 眼鏡。也就是說 Handler 滿足一定的要求才可以被執行。
5、ViewResolver:視圖解析器 ViewResolver 負責將處理結果生成 View 視圖,ViewResolver 首先根據邏輯視圖名解析成物理視圖名即具體的頁面地址,再生成View視圖對象,最後對View進行渲染將處理結果通過頁面展示給用戶。
工作原理解釋說明:
1、用戶發送請求到 SpringMVC 框架提供的 DispatcherServlet 這個前端控制器(瞭解 struts2 的朋友也都知道其實 struts2也有一個前端控制器 web.xml 中的 filter 標簽就是)。
2、前端控制器會去找處理器映射器(HandlerMapping),處理器映射器根據請求 url 找到具體的處理器,生成處理器對象及處理器攔截器(如果有則生成)一併返回給 DispatcherServlet 。
3、根據處理器映射器返回的處理器,DispatcherServlet 會找“合適”的處理器適配器(HandlerAdapter)
4、處理器適配器 HandlerAdpater 會去執行處理器(Handler 開發的時候會被叫成 Controller 也叫後端控制器在 struts2 中action 也是一個後端控制器)執行之前會有轉換器、數據綁定、校驗器等等完成上面這些才會去正在執行 Handler
5、後端控制器 Handler 執行完成之後返回一個 ModelAndView 對象 。
6、處理器適配器 HandlerAdpater 會將這個 ModelAndView 返回前端控制器 DispatcherServlet。前端控制器會將ModelAndView 對象交給視圖解析器 ViewResolver。
7、視圖解析器 ViewResolver 解析 ModelAndView 對象之後返回邏輯視圖。
8、前端控制器 DispatcherServlet 對邏輯視圖進行渲染(數據填充)之後返回真正的物理 View 並響應給瀏覽器。
SpringMVC 配置
1、前端控制器需要在 web.xml 中配置
<!-- 配置前端控制器 -->
<servlet>
<servlet-name>web-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--載入前端控制器配置文件 上下文配置位置-->
<init-param>
<!-- 備註:
contextConfigLocation:指定 SpringMVC 配置的載入位置,如果不指定則預設載入
WEB-INF/[DispatcherServlet 的 Servlet 名字]-servlet.xml
-->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/spring-*.xml</param-value>
</init-param>
<!-- 表示隨WEB伺服器啟動 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>web-dispatcher</servlet-name>
<!-- 備註:可以攔截三種請求
第一種:攔截固定尾碼的url,比如設置為 *.do、*.action, 例如:/user/add.action 此方法最簡單,不會導致靜態資源(jpg,js,css)被攔截
第二種:攔截所有,設置為/,例如:/user/add /user/add.action此方法可以實現REST風格的url,
很多互聯網類型的應用使用這種方式.但是此方法會導致靜態文件(jpg,js,css)被攔截後不能正常顯示.需要特殊處理
第三種:攔截所有,設置為/*,此設置方法錯誤,因為請求到Action,當action轉到jsp時再次被攔截,提示不能根據jsp路徑mapping成功
-->
<!-- 預設匹配所有的請求 -->
<url-pattern>/</url-pattern>
</servlet-mapping>
2、在 spring/spring-web.xml
配置視圖解析器
<!-- 配置視圖解析器 -->
<!-- InternalResourceViewResolver:支持JSP視圖解析 -->
<!-- viewClass:JstlView 表示JSP模板頁面需要使用JSTL標簽庫,所以classpath中必須包含jstl的相關jar包; -->
<!-- prefix 和 suffix:查找視圖頁面的首碼和尾碼,最終視圖的址為: -->
<!-- 首碼+邏輯視圖名+尾碼,邏輯視圖名需要在controller中返回ModelAndView指定,比如邏輯視圖名為hello,-->
<!-- 則最終返回的jsp視圖地址 "WEB-INF/jsp/hello.jsp" -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 決定視圖類型,如果添加了jstl支持(即有jstl.jar),那麼預設就是解析為jstl視圖 -->
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<!-- 視圖首碼 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 視圖尾碼 -->
<property name="suffix" value=".jsp" />
</bean>
3、在 spring/spring-web.xml
配置 註解模式
<!-- 自動載入RequestMappingHandlerMapping和RequestMappingHandlerAdapter, -->
<!-- 可用在xml配置文件中使用<mvc:annotation-driven>替代註解處理器和適配器的配置。 -->
<mvc:annotation-driven/>
4、在 spring/spring-web.xml
配置 掃描web 相關的 bean
<!-- 組件掃描器:可以掃描 @Controller、@Service、@Repository 等等 -->
<context:component-scan base-package="com.controller" />
SpringMVC 中的註解
@Controller
@Controller 註解,用於標識這個類是一個後端控制器(類似struts中的action),主要作用就是接受頁面的參數,轉發頁面。
@Controller 源碼:
@Target({ElementType.TYPE}) // 表明只能定義在類上面
@Retention(RetentionPolicy.RUNTIME) //保留策略是RUNTIME,在JVM載入類時,會把註解載入到JVM記憶體中(它是唯一可以用反射來讀取註解的策略)
@Documented //@Documented用於描述其它類型的annotation應該被作為被標註的程式成員的公共API,因此可以被例如javadoc此類的工具文檔化。Documented是一個標記註解,沒有成員。
@Component //spring框架規定當一個類不好歸類(service、dao、controller)的時候可以使用這個註解,由此可見即便好歸類內部還是使用的@Component註解
public @interface Controller {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* @return the suggested component name, if any
*/
String value() default "";
}
@RequestMapping
這個註解的作用目標就跟@Controller不一樣了,這個註解可以定義在類上面也可以定義在方法上面。
/**
* 1.@RequestMapping:除了修飾方法,還可以修飾類
* 2.類定義處:提供初步的請求信息映射.相對於WEB應用的根目錄(窄化請求)
* 3.方法處:提供進一步的細分映射信息。相對於類定義處的URL。
* 若類定義處為標註@RequestMapping,則方法出的URL相對於WEB應用的根目錄
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
String[] value() default {};
RequestMethod[] method() default {}; //限制請求方式
String[] params() default {}; //要求請求的URL包含指定的參數
}
代碼實例
@Controller
@RequestMapping("/demo")
public class IndexController {
@RequestMapping(value = "/test", method = RequestMethod.GET)
public String index(Model model, HttpServletRequest request) {
// 在游覽器訪問 http://localhost:8080/demo/test 將進入這裡
model.addAttribute("originURL", "");
model.addAttribute("controllerName", "index");
return "index";
}
}
@RequestMapping 還支持 Ant 方格的請求
?:匹配文件中的一個字元
*:匹配文件中任意字元
**:**匹配多層路徑
/user/*/createUser : 匹配 -/user/aa/createUser 或者 -/user/aa/createUser
/user/**/createUser : 匹配 -/user/aa/createUser 或者 -/user/createUser 或者 -/user/aa/cc/createUser
/user/createUser?? : 匹配 -/user/aa/createUseraa
@PathVariable
@PathVariable 這個註解支持現在當下較為流行的 Restful 風格的 URL。 先說說這個註解的作用,支持將 url 中的占位符參數綁定到目標方法的參數上, 該功能也是 SpringMVC 實現 Restful 風格 url 的重要措施。
代碼實例
// http://localhost:8080/demo/sss
@RequestMapping(value = "/{slug:.+}", method = RequestMethod.GET)
public String index2(@PathVariable("slug") String slug, Model model) {
LOG.info("DemoController index2 slug " + slug);
// common
model.addAttribute("originURL", "demo/");
model.addAttribute("controllerName", "demo");
model.addAttribute("controllerMethod", "index2");
model.addAttribute("slug", slug);
return "demo";
}
//slug = sss
我們熟悉的請求應該是 POST 和 GET 請求,這兩個請求也是最常用的而實際上 HTTP1.1 請求還有 PUT、DELETE 等8種來表名請求的動作。
在 SpringMVC 中要實現 PUT 和 DELETE 請求需要在 web.xml 額外配置一個過濾器,這個過濾器的作用就是把 POST 請求變為 PUT 和 DELETE 請求。
關於 Restful 的內容計劃單獨寫。
@RequestParam
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
String value() default "";//值即為請求參數的參數名
boolean required() default true;//該參數是否是必須。預設值為true
String defaultValue() default ValueConstants.DEFAULT_NONE;//請求參數的預設值
}
// http://localhost:8080/demo/para?slug=google
@RequestMapping(value = "/para", method = RequestMethod.GET)
public String index3(@RequestParam(value = "slug", defaultValue = "") String slug, Model model) {
model.addAttribute("originURL", "demo/");
model.addAttribute("controllerName", "demo");
model.addAttribute("controllerMethod", "index3");
model.addAttribute("slug", slug);
return "demo";
}
slug = google
另外還有一點要提示一下,參數沒有加這個註解也能映射成功,這是應為 SpringMVC 框架支持請求參數和目標方法參數一致的時候可以省略這個註解。
@ResponseBody
/**
* Annotation that indicates a method return value should be bound to the web
* response body. Supported for annotated handler methods in Servlet environments.
*
* 這個註解指明一個方法的返回值應該綁定在 web response body 中,在 Servlet 環境中支持註解處理方法
*
* <p>As of version 4.0 this annotation can also be added on the type level in
* which case it is inherited and does not need to be added on the method level.
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseBody {
}
代碼
// http://localhost:8080/demo/json
@RequestMapping(value = "/json", method = RequestMethod.POST)
public @ResponseBody Domain index7(HttpServletRequest request, Model model) {
LOG.info("DemoController demo index7");
model.addAttribute("originURL", "demo/");
model.addAttribute("controllerName", "demo");
model.addAttribute("controllerMethod", "index7");
Domain domain = new Domain();
domain.setDomain("gggoogle.com");
domain.setId(100);
return domain;
}
/* response body
{
"id": 100,
"domain": "gggoogle.com"
}
*/
SpringMVC 數據綁定
簡單說一下場景:
對於一個註冊頁面有很多信息譬如:用戶名、密碼、確認密碼、郵箱、手機、興趣等等。這時候就會想能不能將這些個參數包裝在一個對象中(POJO),用這個POJO來做目標方法的形參上面。可以說的是 SpringMVC 是支持將 POJO 作為目標參數的。當然也是要遵循一些規則的,就是表單的 name 屬性值要和 POJO 的屬性值要一致。當然了,這樣又會有一個新的疑問支不支持級聯屬性答案是支持的。
public class Address {
private String city;
...
}
public class Persion {
private String name;
private Address address;
...
}
<form action="/demo/pojo">
NAME:<input type="text" name="name" />
CITY:<input type="text" name="address.city" />
</form>
@RequestMapping(value = "/pojo", method = RequestMethod.POST)
public String index4(Persion persion, Model model) {
model.addAttribute("originURL", "demo/");
model.addAttribute("controllerName", "demo");
model.addAttribute("controllerMethod", "index4");
model.addAttribute("persion", persion);
return "demo";
}
SpringMVC 使用 Servlet API
可以使用 Servlet 原生的API作為目標方法的參數。具體支持以下類型:HttpServletRequest、HttpServletResponse、HttpSession、java.security.Principal、Locale、InputStream、OutputStream、Reader、Writer
// http://localhost:8080/demo/req?slug=facebook
@RequestMapping(value = "/req", method = RequestMethod.GET)
public String index5(HttpServletRequest request, Model model) {
String slug = request.getParameter("slug");
model.addAttribute("originURL", "demo/");
model.addAttribute("controllerName", "demo");
model.addAttribute("controllerMethod", "index5");
model.addAttribute("slug", slug);
return "demo";
}
Reference: