Spring的MVC詳解——模型視圖、控制器、分發器

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就開始處理請求,具體步驟如下:

  1. 搜索WebApplicationContext,並將它綁定到請求的一個屬性上,以便控制器能夠使用WebApplicationContext。
  2. 綁定本地化信息、主題信息等信息。
  3. 搜索合適的處理器,並準備ModelAndView。
  4. 業務邏輯處理完畢後,根據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>

 

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