什麼是SpringMVC?SpringMVC是一個和Struts2差很少的東西,他們的做用和性質幾乎是相同的,甚至開發效率上也差很少,可是在運行效率上SpringMVC要高於Struts2;注意這裏的SpringMVC很明確的指明瞭使用了MVC框架,Struts2也使用了MVC框架。html
和以前使用的spring環境幾乎是差很少的,可是須要增長兩個核心包:前端
org.springframework.web.servlet-3.0.0.RELEASE.jarjava
org.springframework.web-3.0.0.RELEASE.jargit
這是在Spring3.0的環境下,Spring3.2有了重大的升級,再也不使用org.springframework.web.servlet-3.0.0.RELEASE.jar,而是取而代之的使用了下面的jar包:github
spring-webmvc-3.2.0.RELEASE.jarweb
固然,仍是推薦使用Spring3.2的版本,根據spring官方文檔中的聲明,自3.0版本以後一直到3.2版本的全部版本都存在已知的並且很明顯的bug;在spring3.2中則解決了至關數量的bug。spring
因爲我使用的是Eclipse,因此相關jar包IDE並無提供支持,因此只能手動添加jstl.jar以及standard.jar了:express
aopalliance.jar
commons-logging.jar
jstl.jar
spring-aop-3.2.0.RELEASE.jar
spring-aspects-3.2.0.RELEASE.jar
spring-beans-3.2.0.RELEASE.jar
spring-context-3.2.0.RELEASE.jar
spring-context-support-3.2.0.RELEASE.jar
spring-core-3.2.0.RELEASE.jar
spring-expression-3.2.0.RELEASE.jar
spring-web-3.2.0.RELEASE.jar
spring-webmvc-3.2.0.RELEASE.jar
standard.jar編程
很明顯了,使用SpringMVC的另一個最大的好處就是不用像在Struts2中那樣再引入一大批的jar包了。瀏覽器
<servlet> <servlet-name>action</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.action</url-pattern> </servlet-mapping>
默認的Servlet將會讀取WEB-INF文件夾下的相應配置文件;固然該配置文件的命名還須要知足規則才行:$servletName-servlet.xml,不然Servlet也找不到相應的配置文件;根據上述的配置中的servlet-name標籤的值,咱們將配置文件的名字命名爲:action-servlet.xml,這個配置文件實際上就是Spring的配置文件
首先配置「內部資源視圖解析器」
<?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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation=" http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd 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 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd"> <!-- 內部資源視圖解析器 --> <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/jsps/"></property> <property name="suffix" value=".jsp"></property> </bean> <beans>
內部視圖資源解析器肯定了一種規則,那就是將Controller返回的ModelAndView對象進行解析,並將viewName提取出來和"prefix"以及"suffix"進行拼接,獲得須要跳轉的資源位置,並執行跳轉,具體是重定向仍是轉發,須要根據ModelAndView的內容進行肯定。
這裏則是假設全部jsp頁面都保存到了/jsps目錄下,而且都是以.jsp結尾。
全部控制器的超類都是AbstractController,這裏直接繼承該類,並重寫該類的核心方法:handleRequestInternal,該方法返回值是ModelAndView對象,該對象必須賦予一個字符串表明資源的位置。
package com.kdyzm.controller; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.AbstractController; public class HomeController extends AbstractController { @Override protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception { System.out.println("你好,這是第一個SpringMVC程序!"); return new ModelAndView("index"); } }
這裏的index通過"內部視圖資源解析器"解析以後,就轉變成了真正的地址:/jsps/index.jsp,在瀏覽器上就須要使用http://localhost:8080/項目名稱/jsps/index.jsp來訪問了。
新建控制器以後須要將控制器注入到spring容器管理。
<bean name="/home.action" class="com.kdyzm.controller.HomeController"></bean>
1 <%@ page language="java" pageEncoding="utf-8"%> 2 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 3 <html> 4 <head> 5 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 6 <title>Insert title here</title> 7 </head> 8 <body> 9 你好,這是第一個SpringMVC程序! 10 </body> 11 </html>
測試成功。
所謂的處理器映射就是一種Map對象,它儲存着請求的url到控制器的映射,它可以將咱們的請求轉交給某一個指定的控制器處理。
這是默認的url處理器映射,也是最常使用的url處理器映射;所謂默認,就是不配置也會自動加載,這裏顯式聲明一下意思意思:
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"> <property name="order" value="3"></property> </bean>
它有一個serOrder方法,該方法可以肯定該url處理器的優先級,若是配置了多個url處理器映射,那麼該值越大的越有限匹配;以前的例子因爲沒有明確的配置url處理器映射,因此使用的是默認的url處理器映射。
這種url處理器映射基本上沒有人使用,由於使用這種方式須要將key和值一一列舉出來,這種工做量即便在一箇中型項目中也是難以承受的。
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="order" value="2"></property> <property name="mappings"> <map> <entry key="/abc.action" value-ref="homeAction"></entry> <entry key="/xiaozhang.action" value-ref="homeAction"></entry> </map> </property> </bean>
使用這種形式的訪問方式就是使用key值部分,好比上述的例子中定義了兩個能夠都指向了同一個資源:homeAction,那麼使用這兩種方式都可以訪問到同一個頁面:
http://localhost:8080/springmvc1/abc.action或者http://localhost:8080/springmvc1/xiaozhang.action
顧名思義,使用這種方式可以直接經過控制器的類名訪問控制器,使用這種方式雖然簡單可是使用這種凡有着很明顯的弊端,那就是若是不一樣的包中若是定義了相同類名的控制器,那麼使用這種方式確定會出錯。因此也不推薦使用這種方式。
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"> <property name="order" value="1"></property> </bean>
使用控制器類名直接訪問控制器,可是注意須要將類名首字母編程小寫:
SpringMVC中控制器的概念和Struts2中的Action的概念幾乎是差很少的,嘗試着將控制器看作Action會發現理解起來就會簡單的多。
直接繼承AbstractController,該抽象類是全部控制器的超類,直接繼承該抽象類也可以實現簡單的數據處理功能。
1 package com.kdyzm.controller; 2 3 import javax.servlet.http.HttpServletRequest; 4 import javax.servlet.http.HttpServletResponse; 5 6 import org.springframework.web.servlet.ModelAndView; 7 import org.springframework.web.servlet.mvc.AbstractController; 8 9 public class HomeController extends AbstractController { 10 @Override 11 protected ModelAndView handleRequestInternal(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception { 12 System.out.println("這是第二個SpringMVC程序!"); 13 return new ModelAndView("index"); 14 } 15 }
在XML文件中的配置:
<bean id="homeAction" name="/home.action" class="com.kdyzm.controller.HomeController"></bean>
在講解接下來的幾個控制器以前首先須要建立一個JavaBean:
com.kdyzm.domain.Person1 package com.kdyzm.domain; 2 3 import java.io.Serializable; 4 5 public class Person implements Serializable { 6 private static final long serialVersionUID = 1298989747316274828L; 7 private int id; 8 private String name; 9 private String address; 10 private int age; 11 12 /****************** 華麗的分割線 **************************/ 13 public int getId() { 14 return id; 15 } 16 17 public void setId(int id) { 18 this.id = id; 19 } 20 21 public String getName() { 22 return name; 23 } 24 25 public void setName(String name) { 26 this.name = name; 27 } 28 29 public String getAddress() { 30 return address; 31 } 32 33 public void setAddress(String address) { 34 this.address = address; 35 } 36 37 public int getAge() { 38 return age; 39 } 40 41 public void setAge(int age) { 42 this.age = age; 43 } 44 45 @Override 46 public String toString() { 47 return "Person [id=" + id + ", name=" + name + ", address=" + address + ", age=" + age + "]"; 48 } 49 }
使用這種控制器可以實現簡單的數據的收發,而且可以實現自動封裝成Bean對象;可是須要經過構造方法註冊命令類和命令名稱。
1 package com.kdyzm.controller; 2 3 import javax.servlet.http.HttpServletRequest; 4 import javax.servlet.http.HttpServletResponse; 5 6 import org.springframework.validation.BindException; 7 import org.springframework.web.servlet.ModelAndView; 8 import org.springframework.web.servlet.mvc.AbstractCommandController; 9 10 import com.kdyzm.domain.Person; 11 12 /** 13 * 命令控制器 14 * 15 * @author kdyzm 16 * 訪問形式:http://localhost:8080/springmvc1/commandController.action?id=1& 17 * name=xiaozhang&age=12&address=shandong 18 */ 19 @SuppressWarnings("deprecation") 20 public class MyCommandController extends AbstractCommandController { 21 /** 22 * 須要經過構造方法註冊命令類和命令名稱 23 */ 24 public MyCommandController() { 25 this.setCommandClass(Person.class); 26 this.setCommandName("person"); 27 } 28 29 @Override 30 protected ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object command, 31 BindException errors) throws Exception { 32 System.out.println("使用了CommandController控制器!" + command); 33 return new ModelAndView("index"); 34 } 35 36 }
須要注意的是使用這種方式必須繼承AbstractCommandController抽象類,可是這種方式已通過時了,也就是不推薦使用了,查看javadoc:
在上面畫圈的部分能夠看出,在spring3.0以後這種方式就已通過時了,推薦使用註解控制器的方式;註解控制器的方式以後再說。
先看XML文件中的配置,XML文件中的配置也十分簡單,只須要注入到Spring容器中便可:
<bean id="commandController" name="/commandController.action" class="com.kdyzm.controller.MyCommandController"></bean>
表單控制器的特色是:
(1)可以自動將獲取的參數封裝成Bean對象
(2)能夠自動識別請求的方式,並根據請求方式的不一樣使用不一樣方式進行處理
若是是GET方式的請求,則會自動轉發到顯示錶單的頁面;若是是POST請求,就會對數據進行封裝並跳轉到程序中定義好的頁面。
1 package com.kdyzm.controller; 2 3 import org.springframework.web.servlet.mvc.SimpleFormController; 4 5 import com.kdyzm.domain.Person; 6 /** 7 * 簡單表單控制器 8 * 訪問方法:http://localhost:8080/springmvc1/formController.action 9 * 表單頁面顯示方法和訪問的表單提交的對象都是相同的url地址 10 * @author kdyzm 11 * 12 */ 13 @SuppressWarnings("deprecation") 14 public class MyFormController extends SimpleFormController { 15 /** 16 * 在構造方法中實現註冊命令類和命令名稱 17 */ 18 public MyFormController() { 19 this.setCommandClass(Person.class); 20 this.setCommandName("person"); 21 System.out.println("執行了formController的構造方法!"); 22 } 23 /** 24 * 執行set、get方法的時候會出問題! 25 * 表單內容必須填寫正確,不然不能正確提交!最後執行不了該方法! 26 */ 27 @Override 28 protected void doSubmitAction(Object command) throws Exception { 29 System.out.println("MyFormController:"+command); 30 super.doSubmitAction(command); 31 } 32 }
注意上面的方法的返回值是void,該方法的返回值爲何不是ModelAndView對象呢?由於成功以後須要跳轉的頁面是由配置文件決定的。
XML文件中的配置和以前相比有些不一樣:
<bean id="formController" name="/formController.action" class="com.kdyzm.controller.MyFormController"> <property name="formView" value="formView"></property> <property name="successView" value="index"></property> </bean>
注意上面紅色背景部分的內容,formView部分配置的是若是接收到了GET方式的請求,就會重定向到formView.jsp頁面上,若是接收到了POST請求,那麼就會執行上面的doSubmitAction方法,若是執行該方法的過程當中並無出現異常,執行該方法以後就會跳轉到配置的成功頁面上去。
注意,在執行該doSubmitAction方法以前就已經將javaBean封裝好了,若是封裝的過程當中出現異常,Spring並不會報錯提醒,只是將頁面跳轉到顯示form表單的頁面上去而已,這會讓人百思不得其解到底發生了什麼事情,實際上就是封裝javaBean失敗而已;最多見的錯誤信息就是"類型不匹配",好比javaBean中定義的類型是int類型,可是在前端頁面上傳遞過來的數據不能轉換成整型類型,這種狀況下就會發生轉換失敗的狀況,這樣就不能跳轉到成功頁面上去了。
這種方式也已通過時了,過期的緣由和以前相同,在spring3.0以後就推薦使用註解控制器實現了。
這種控制器的特色:
(1)可以跨頁面實現表單提交,好比調查問卷,一頁顯示不完,須要多個頁面才能顯示完成,可是提交的時候就須要提交全部的信息
(2)可以自動封裝提交的數據,固然這種功能以前的表單控制器也可以實現
(3)使用這種方式須要特別注意數據回顯的問題
1 package com.kdyzm.controller; 2 3 import javax.servlet.http.HttpServletRequest; 4 import javax.servlet.http.HttpServletResponse; 5 6 import org.springframework.validation.BindException; 7 import org.springframework.web.servlet.ModelAndView; 8 import org.springframework.web.servlet.mvc.AbstractWizardFormController; 9 10 import com.kdyzm.domain.Person; 11 /** 12 * 嚮導表單控制器 13 * @author kdyzm 14 * 15 */ 16 @SuppressWarnings("deprecation") 17 public class MyWizardController extends AbstractWizardFormController{ 18 /** 19 * 仍是須要經過構造方法註冊命令類和命令方法 20 */ 21 public MyWizardController() { 22 this.setCommandClass(Person.class); 23 this.setCommandName("person"); 24 } 25 /** 26 * 當最終提交了表單以後將會訪問該方法 27 */ 28 @Override 29 protected ModelAndView processFinish(HttpServletRequest request, HttpServletResponse response, Object command, 30 BindException errors) throws Exception { 31 System.out.println("訪問了WizardController類:"+command); 32 return new ModelAndView("index"); 33 } 34 @Override 35 protected ModelAndView processCancel(HttpServletRequest request, HttpServletResponse response, Object command, 36 BindException errors) throws Exception { 37 System.out.println("訪問了processCancel方法!"+command); 38 return new ModelAndView("index"); 39 } 40 }
固然,使用這種方式也得經過構造方法實現註冊命令類和命令方法;關鍵是得重寫上面紅色背景部分的方法,這兩個方法分別對應着提交的方法和取消的方法,並且返回值都是ModelAndView,這就意味着不須要咱們經過配置文件的方式實現頁面的跳轉了,這時候就使用ModelAndView的方式實現頁面的跳轉。
配置文件和以前的配置文件也有比較大的區別:
<bean id="wizardFormController" name="/wizard.action" class="com.kdyzm.controller.MyWizardController"> <property name="pages"> <list> <value>wizard/1</value> <value>wizard/2</value> <value>wizard/3</value> </list> </property> </bean>
這裏的pages屬性指的就是全部的分頁;wizard/1實際上就是/jsps/wizard/1.jsp,也就是說我在/jsps/wizard文件夾中新建了三個jsp文件,這三個jsp文件組成了"提交"表單這一事件。
在每一個jsp頁面中,須要視狀況給它們分配幾個"提交"按鈕,好比上一頁、下一頁、退出、提交,可是每一個提交按鈕都須要給它們分配一個預先定義好的名字;這個名字不是根據咱們的規則定義的,而是根據AbstractWizardFormController類的規則定義的。
org.springframework.web.servlet.mvc.AbstractWizardFormController
Deprecated. as of Spring 3.0, in favor of annotated controllers
Form controller for typical wizard-style workflows.
In contrast to classic forms, wizards have more than one form view page. Therefore, there are various actions instead of one single submit action:
- finish: trying to leave the wizard successfully, that is, perform its final action, and thus requiring a valid state;
- cancel: leaving the wizard without performing its final action, and thus without regard to the validity of its current state;
- page change: showing another wizard page, e.g. the next or previous one, with regard to "dirty back" and "dirty forward".
Finish and cancel actions can be triggered by request parameters, named PARAM_FINISH ("_finish") and PARAM_CANCEL ("_cancel"), ignoring parameter values to allow for HTML buttons. The target page for page changes can be specified by PARAM_TARGET, appending the page number to the parameter name (e.g. "_target1"). The action parameters are recognized when triggered by image buttons too (via "_finish.x", "_abort.x", or "_target1.x").
The current page number will be stored in the session. It can also be specified as request parameter PARAM_PAGE ("_page") in order to properly handle usage of the back button in a browser: In this case, a submission will always contain the correct page number, even if the user submitted from an old view.
The page can only be changed if it validates correctly, except if a "dirty back" or "dirty forward" is allowed. At finish, all pages get validated again to guarantee a consistent state.
Note that a validator's default validate method is not executed when using this class! Rather, the
validatePage
implementation should call specialvalidateXXX
methods that the validator needs to provide, validating certain pieces of the object. These can be combined to validate the elements of individual pages.Note: Page numbering starts with 0, to be able to pass an array consisting of the corresponding view names to the "pages" bean property.
上述文檔說明了規則:
(1)若是是"提交"按鈕,名字必須是"_finish"
(2)若是是"退出"按鈕,名字必須是"_cancle"
(3)若是是"上一頁"或者"下一頁"按鈕,名字的前綴必須是"_target",後面跟着目標頁的頁碼,規律是若是是第一頁,那麼頁碼就是0;若是是第二頁,頁碼就是1,以此類推。
另外以前配置文件中的
<value>wizard/1</value> <value>wizard/2</value> <value>wizard/3</value>
並無什麼特別的含義,並不是寫了1就是第一頁,其實任何名字也無所謂,實際上擺放的位置決定了它真實的頁序。
注意:全部過程當中訪問的url地址都是:http://localhost:8080/springmvc1/wizard.action,至於嚮導表單控制器是如何進行邏輯處理的,這種事情就不須要咱們操心了。