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>

 

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