Spring MVC的模型和視圖(ModelAndView)
對於實現MVC的框架來說,它們的重點都在View和Controller上,而Model層基本上都沒有什麼實現。Model層一般採用面向對象技術來實現,這並不需要MVC框架來做什麼。Spring僅僅提供了ModelAndView類,來利用Map存儲Model層處理後的結果集合。
模型和視圖
Spring提供的org.springframework.web.servlet.ModelAndView類,所有的控制器都會返回一個ModelAndView,用來負責傳遞Model層處理後的結果集合和指定View層的信息。ModelAndView的部分代碼如下:
//**********ModelAndView.java************
public class ModelAndView {
private Object view; //用來存儲一個視圖類或者視圖名稱
private Map model; //用來存儲模型層處理後的結果集合,使用Map接口
......
//只指定要返回的試圖層,表示視圖層不需要返回結果集合。
public ModelAndView(View view) {
this.view = view;
}
//只指定要返回的試圖層名稱,具體的路徑可以在配置文檔中設定。
public ModelAndView(String viewName) {
this.view = viewName;
}
//指定要返回的視圖層,並將結果集合存放在Map中,供視圖層顯示使用。
public ModelAndView(View view,Map model) {
this.view = view;
this.model = model;
}
//指定要返回的視圖層名稱,路徑在配置文件中指定,並將結果集合存放在Map中,供視圖層顯示使用
public ModelAndView(String viewName,Map model) {
this.view = viewName;
this.model = model;
}
//指定要返回的視圖層,並將供視圖層顯示的結果和存放結果的名稱通過addObject存放在Map中。
public ModelAndView(View view,String modelName,Object modelObject) {
this.view = view;
addObject(modelName,modelObject);
}
//指定要返回的視圖層的名稱,路徑在配置文件中指定,供視圖層顯示的結果和存放結果的名稱通過addObject存放在Map中。
public ModelAndView(String viewName,String modelName,Object modelObject) {
this.view = viewName;
addObject(modelName,modelObject);
}
//獲取model
public Map getModel() {
if(this.model == null) {
this.model = new HashMap(1);
}
return this.model;
}
//增加model
public ModelAndView addObject(String modelName,Object modelObject) {
getModel().put(modelName,modelObject);
return this;
}
......
}
在ModelAndView類中參數中定義的View既可以表示一個View的名稱,也可以表示一個實現View接口的類,View接口主要用來爲請求做準備,並將請求結果傳遞給視圖層。View.java代碼如下:
//********View.java**********
public interface View {
String getContentType();
//通過renser將結果傳遞給視圖層
void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception;
}
Spring提供了對衆多視圖層實現的支持,真正做到與視圖層的具體實現無關。實現了很多View接口的實現類,分別表示不同的視圖層處理,比如JSP、velocity、xslt、tiles、freemarker、jasperreports和Jstl等。InternalResourceView就是一個常用的視圖,表示用來處理Servlet和JSP,JstlView繼承InternalResourceView,表示用來處理Jstl。
JSTL
- Jstl(JSP Standard Tag Library)是JSP的標準標記庫。
- Jstl 是實現Web應用程序中通用功能的定製標記庫集,功能包括:條件判斷、循環、XML操作以及數據庫訪問等。
- Jstl 實現大量服務器端Java應用程序常用的基本功能。
在單純的JSP頁面中通常是如下方式來實現條件判斷的:
<% if("gd".equals(compld)) { %>
<p>歡迎您來到gd公司</p>
<% } else if("gf".equals(compld)) { %>
<p>歡迎您來到gf公司</p>
<% } %>
這樣導致在頁面中出現了很多邏輯代碼,維護起來非常麻煩,而通過將常用功能封裝到定製標記庫的標準集合中,Jstl 減少了頁面的負責程度。
Jstl1.0包括4個定製標記庫(core、format、XML、SQL)和2個通用標記庫驗證器(ScriptFreeTLV、PermittedTaglibsTLV):
- core標記庫:提供了定製操作。
- fromat標記庫:定義了用來格式化數字和日期的操作,還可以使用本地化資源對JSP頁面進行國際化支持。
- XML標記庫:用來操作通過XML表示的數據。
- SQL標記庫:定義了用來查詢關係數據庫的操作。
- ScriptFreeTLV驗證器:可以在JSP頁面中禁止使用各種類型的JSP腳本元素(scriptlet、表達式等)。
- PermittedTaglibsTLV驗證器:可以用來限制由應用程序的JSP頁面訪問的定製標記庫集。
Jstl1.0的4個標記庫都有使用JSP表達式指定動態屬性值的備用版本:
- 使用表達式語言的被稱爲EL庫。
- 備用庫依賴JSP的更傳統的請求時屬性值被稱爲RT庫。
Jstl EL庫一些特定的語法:
- EL表達式定界符的Jstl操作:<c:out value="${msg}"/>;之前這樣編寫PGN代碼:<%=request.getParameter("msg")%>。
- 利用運算符的EL表達式的示例代碼:${(i >= 1)&&(i <= 5)}
- Jstl core庫EL版本的taglib僞指令代碼:<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
- 使用的運算符:
- (1)算術運算符:+、-、*、/、%
- (2)關係運算符:==、!=、<</code>(或lt)、>(或gt)、<=(或le)、>=(或ge)
- (3)邏輯運算符:&&(或and)、||(或or)、!(或not)
- (4)驗證運算符:empty
Jstl RT庫一些特定的語法:
- EL表達式定界符的Jstl操作:<c:out value="expression" default="expression" escapeXml="boolean"/>。
- Jstl core庫EL版本的taglib僞指令代碼:<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>
視圖解析
Spring實現與視圖層的具體實現無關,是通過視圖解析器來實現的。在Spring中接口ViewResolver提供了視圖名和實際視圖之間的映射。代碼如下:
//**************ViewResolver.java***************
public interface ViewResolver {
//負責解析視圖名稱
View resolveViewName(String viewName,Locale locale) throws Exception;
}
InternalResourceViewResolver是常用的一個視圖解析器,在Spring的配置文檔中通過ViewClass來指定具體的視圖類。
Spring MVC的控制器(Controller)
控制器負責建立模型層和視圖層之間的聯繫。
Controller架構
需要定義接口 org.springframework.web.servlet.mvc.Controller ,所有Spring提供的控制器和自定義的控制器都必須實現這個接口。
//************Controller.java************
public interface Controller {
//所有的控制器實現類都必須實現這個方法
ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
}
Spring MVC的分發器(DispatcherServlet)
Spring設計圍繞一個能夠請求分發到控制器的Servlet,即DispatcherServlet。這個Servlet在Spring中是通過配置文檔來定義的。
在web.xml中的定義方式:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1"
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">
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--初始化參數,用contextConfigLocation指定配置文件的位置-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/dispatcherServlet-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!--攔截所有以do結尾的請求都由DispatcherServlet來處理-->
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
如果初始化時,具有多個配置文件位置時,可以用英文逗號分隔。
<!--初始化參數,用contextConfigLocation指定配置文件的位置-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/a-servlet.xml,/WEB-INF/b-servlet.xml</param-value>
</init-param>
Spring默認爲servlet-name + "-servlet.xml",並放在WEB-INF目錄下。
分發器工作流程:
當DispatcherServlet配置好後,一旦DispatcherServlet接收到請求,DispatcherServlet就開始處理請求,具體步驟如下:
- 搜索WebApplicationContext,並將它綁定到請求的一個屬性上,以便控制器能夠使用WebApplicationContext。
- 綁定本地化信息、主題信息等信息。
- 搜索合適的處理器,並準備ModelAndView。
- 業務邏輯處理完畢後,根據WebApplicationContext中綁定的視圖信息顯示對應的視圖。
分發器與視圖解析器的結合:
指定viewResolver的Bean的viewClass屬性後,還需要在helloWorldAction的Bean下配置viewPage屬性來指定返回頁面的絕對路徑。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
......
<!--定義視圖及JSP存放的路徑-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass">
<value>org.springframework.web.servlet.view.InternalResourceView</value>
</property>
</bean>
<!--定義控制器-->
<bean id="helloWorldAction" class="com.gc.action.HelloWorldAction">
<property name="helloWorld">
<value>HelloWorld</value>
</property>
<property name="viewPage">
<value>/WEB-INF/jsp/index.jsp</value>
</property>
</bean>
</beans>
當然,也可以在viewResolver的Bean下配置prefix和suffix屬性,分別設置路徑和後綴,然後在helloWorldAction的Bean下viewPage屬性中只指定返回的頁面名稱即可。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
......
<!--定義視圖及JSP存放的路徑-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass">
<value>org.springframework.web.servlet.view.InternalResourceView</value>
</property>
<!--JSP存放的目錄-->
<property name="prefix">
<value>/WEB-INF/jsp/</value>
</property>
<!--JSP文件的後綴-->
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
<!--定義控制器-->
<bean id="helloWorldAction" class="com.gc.action.HelloWorldAction">
<property name="helloWorld">
<value>HelloWorld</value>
</property>
<property name="viewPage">
<value>index</value>
</property>
</bean>
</beans>
在一個Web應用中使用不同的視圖層技術
<bean id="helloWorldAction" class="com.gc.action.HelloWorldAction">
<property name="helloWorld">
<value>HelloWorld</value>
</property>
<!--指定使用的是JSP/Servlet技術-->
<property name="viewPage1">
<value>index1</value>
</property>
<!--指定使用的是Jstl技術-->
<property name="viewPage2">
<value>index2</value>
</property>
<!--指定使用的是Velocity技術-->
<property name="viewPage3">
<value>index3</value>
</property>
</bean>
在views.properties指定使用的視圖層技術。
index1.class=org.springframework.web.servlet.view.InternalResourceView
index1.url=/WEB-INF/jsp/index1.jsp
index2.class=org.springframework.web.servlet.view.JstlView
index2.url=/WEB-INF/jsp/index2.jsp
index3.class=org.springframework.web.servlet.view.VelocityView
index3.url=/WEB-INF/jsp/index3.jsp
在DispatcherServlet中指定處理異常的頁面
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
......
<!--定義視圖及JSP存放的路徑-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass">
<value>org.springframework.web.servlet.view.JstlView</value>
</property>
<!--JSP存放的目錄-->
<property name="prefix">
<value>/WEB-INF/jsp/</value>
</property>
<!--JSP文件的後綴-->
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
<bean id="exceptionResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="exceptionMapping">
<prop key="java.sql.SQLException">outException</prop>
<prop key="java.sql.IOException">outException</prop>
</property>
</bean>
<!--定義控制器-->
......
</beans>
<html>
<head><title>拋送異常頁面</title></head>
<body>
<%Exception ex = (Exception)request.getAttribute("Exception");%>
<H2>Exception:<%=ex.getMessage();%></H2>
</body>
</html>