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>