springMVC的原理
前提小曲: 自spring升级到3.0后,注解被强化,而且在也相当方便大家的使用。但是呢,个人觉得3.0后全部用注解去开发一个项目工程,对于以后要维护或者接手
开发任务的工程师来说是个麻烦事,因为它不如spring的配置文件那么清晰直观。(我的建议是不使用,如果有不同看法的话,各位朋友我们可以私下讨论,在这里就不用挣了)
转入正题:
Spring MVC的实现原理
Spring提供了DispatcherServlet,这个类不仅负责实现请求转发,还负责启动一个WebApplicationContext容器。
按照Spring一贯的IoC哲学,所有的Controller都是JavaBean,并由IoC容器统一管理。
对于View,则采取了更灵活的处理方式,Spring MVC允许使用不同的View实现,除了JSP外,还可以使用Velocity、Freemaker、XSLT等多种View技术。
总的来讲,要使用Spring 自带的MVC框架,需要以下步骤。
① 在web.xml中配置DispatcherServlet及URL映射。
② 编写IoC容器需要的XML配置文件,命名为<servlet-name>-servlet.xml,放到/WEB-INF目录下。
例如,如果DispatcherServlet在web.xml中的配置名称为dispatcher,则Spring将寻找dispatcher- servlet.xml配置文件。
③ 在XML配置文件中定义URL映射方式和使用哪种View技术。
一、 配置DispatcherServlet
首先,在web.xml中配置DispatcherServlet,并将所有以“.html”结尾的URL全部映射到DispatcherServlet上,这样,用户仅从URL地址上无法得知服务器端后台使用了何种技术。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<!-- spring dispatch servlet -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<!-- 申明taglib,将在JSP中使用 -->
<taglib>
<taglib-uri>http://java.sun.com/jsp/jstl/core</taglib-uri>
<taglib-location>/WEB-INF/c.tld</taglib-location>
</taglib>
</web-app>
对于Web应用程序,Spring的ApplicationContext是由DispatcherServlet加载的,它会在/WEB-INF/下查找一个名称为<servletName> -servlet.xml的XML配置文件来初始化Spring Web应用程序的ApplicationContext。
由于我们在web.xml中定义DispatcherServlet的名称为 dispatcher,
因此相应的XML配置文件就必须是/WEB-INF/dispatcher-servlet.xml。
下一步便是编写dispatcher-servlet.xml配置文件,首先定义URL的映射方式(HandlerMapping)。
Spring提供了几种常用的HandlerMapping。
1.使用SimpleUrlHandlerMapping
SimpleUrlHandlerMapping提供了最简单的URL映射,通过Properties将URL和Controller对应起来,配置示例如下。
<bean id="simpleUrlHandlerMapping" class="org.springframework.web.servlet. handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/a.html">controllerA</prop>
<prop key="/b.html">controllerB</prop>
</props>
</property>
</bean>
这种方式和Struts的配置类似。当用户请求一个URL时,Spring就在SimpleUrl HandlerMapping注入的Properties中查找对应的Controller。
2.使用BeanNameUrlHandlerMapping
BeanNameUrlHandlerMapping的实现更为简单,每个Controller的URL与其name属性对应,因此,只需要对每个Controller以URL作为name,就可以实现URL映射。配置示例如下。
<bean id="beanNameUrlHandlerMapping" class="org.springframework.web.servlet. handler.BeanNameHandlerMapping" />
<bean name="/a.html" class="example.chapter7.ControllerA" />
<bean name="/a.html" class="example.chapter7.ControllerB" />
之所以用Bean的name作为URL而不是id,是因为XML规范不允许在id标识中使用特殊字符“/”。当用户请求一个URL时,Spring将直接查找name为URL的Controller。
使用 SimpleUrlHandlerMapping的麻烦之处在于,添加或删除Controller时必须要对 SimpleUrlHandlerMapping做相应的修改,而BeanNameUrlHandlerMapping则无需手工编写映射,只需要在每个 Controller中仔细定义name属性。
我们推荐首先考虑使用BeanNameUrlHandlerMapping。事实上,如果没有在XML配置文件中定义任何 UrlHandlerMapping,则Spring MVC默认使用BeanNameUrlHandlerMapping。
Spring还提供了一个 ControllerClassNameHandlerMapping,它和BeanName UrlHandlerMapping类似,不过是将Controller的ClassName和对应的URL关联起来,由于这种方式灵活性欠佳,实际使用较少。
也可以混合使用多种 UrlHandlerMapping,但是必须为每个UrlHandlerMapping指定order属性来表示优先级,order值越小优先级越高, Spring会先查询优先级高的UrlHandlerMapping。若找到了对应的Controller,就不再继续查询,否则,按照优先级依次查询, 直到找到为止。若所有的UrlHandlerMapping都无法返回一个合适的Controller,并且没有设置默认的Controller时,就会 返回给客户端一个“404 Not Found”错误,表示不存在这个URL。
下一步需要为Spring MVC指定一个ViewResolver(视图解析器),指示使用何种视图技术,以及如何解析ModelAndView返回的逻辑视图名称。
这里我们直接给出使用JSP视图的配置,对于其他的视图技术将会在后面讲到。
<bean id="viewResolver" class="org.springframework.web.servlet.view. InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet. view.JstlView" />
<property name="prefix" value="/" />
<property name="suffix" value=".jsp" />
</bean>
prefix和suffix将和逻辑视图名称一起组合成为实际视图的路径。
例如,对于上例,若返回一个new ModelAndView("test", model),则实际的视图路径由prefix+逻辑视图名+suffix这3部分构成。
/test.jsp
定义 前缀(prefix)使得视图文件无论放在何处都可以通过修改前缀来实现位置无关性(当然,必须在web目录内),许多应用程序将其放在/WEB-INF 目录下,使得用户无法通过URL直接访问视图文件以保证视图文件的安全;
定义后缀(suffix)可以在将来用另一种视图技术(例如,Velocity)取代现在的JSP视图,只需将后缀从“.jsp”更改为“.vm”即可,而不必更改源代码中的逻辑视图名。总之,一切目标都是为了实现最大限度的解耦。
二、实现Controller
实现了org.springframework.web.servlet.mvc.Controller接口的Bean都可以作为有效的Controller来处理用户请求。例如,一个最简单的TestController。
public class TestController implements Controller {
public ModelAndView handleRequest(HttpServletRequest request, HttpServlet Response response) throws Exception {
String name = request.getParameter("name");
if(name==null)
name = "spring";
Map model = new HashMap();
model.put("name", name);
model.put("time", new Date());
return new ModelAndView("test", model);
}
}
注 意,上例的Controller接口和返回值ModelAndView都是在Spring框架中定义的,这和SimpleMVC项目中我们自己定义的 Controller接口和ModelAndView类所在的包是不同的。在SimpleMVC项目中,我们并没有使用Spring MVC,而是借用Spring MVC的概念自定义接口。在现在的SpringMVC项目中,我们没有自定义任何接口,而是直接使用Spring MVC框架来实现Web应用程序,这一点请读者务必区分清楚。
另外需要注意的是,ModelAndView返回的逻辑视图是“test”,还记得在viewResolver中定义的prefix和suffix吗?实际的视图名称由这3部分构成便是“/test.jsp”。
最后将这个TestController作为Bean定义在dispatcher-servlet.xml中,由于我们准备使用默认的BeanNameUrlHandlerMapping,因此,需要在Bean的name中指定URL。
<bean name="/test.html" class="example.chapter7.TestController" />
完整的dispatcher-servlet.xml配置文件的内容如下。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="/test.html" class="example.chapter7.TestController" />
<bean id="viewResolver" class="org.springframework.web.servlet.view. InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet. view.JstlView" />
<property name="prefix" value="/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
注意,我们没有指定UrlHandlerMapping,Spring会自动使用默认的BeanNameUrl HandlerMapping。
三、 实现View
到目前为止,我们已经编写了 Controller的实现和配置文件,最后一步是编写一个JSP文件作为视图。由于采用了MVC架构,视图的任务只有一个,就是将Controller 返回的Model渲染出来。Spring MVC会将Model中的所有数据全部绑定到HttpServlet Request中,然后将其转发给JSP,JSP只需将数据显示出来即可。
通过JSTL标签库能进一步简化显示逻辑,我们看看如何显示TestController返回的Model。test.jsp文件的内容如下。
<%@ page contentType="text/html; charset=utf-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<html>
<head>
<title>SpringMVC</title>
</head>
<body>
<h3>Hello, <c:out value="${name}" />,
it is <c:out value="${time}" /></h3>
</body>
</html>
现在,Spring MVC所需的所有组件都已编写并配置完毕,我们先来回顾一下Spring MVC的处理流程
将SpringMVC工程的编译输出目录设置为/web/WEB-INF/classes,然后编译工程,打开浏览器,测试我们编写的SpringMVC,结果如图7-23所示。

图7-23 
现在,读者对Spring的MVC框架应该有了一个初步的认识。
再补充一些:
- SPRING MVC的方法名解析器InternalPathMethodNameResolver
Spring MVC除了能把 URL 分配到Controller上,还可以进一步解析到方法名上,即要找到的Controller上的哪个方法并执行之。缺省的方法名解析器是InternalPathMethodNameResolver,它根据 URL 样式解析方法名(实际上就是根据URL中的“文件名部分”决定的该方法名,解释器会把整个URL中最后一个”/”和最后一个”.”之间的部分作为要调用的方法名。)
举个例子,配置一个方法名解析器:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <!-- 方法名解析器 --> <bean id="internalPathMethodNameResolver" class="org.springframework.web.servlet.mvc.multiaction.InternalPathMethodNameResolver"/> <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <prop key="/updateUser.html">methodTestController</prop> <prop key="/deleteUser.html">methodTestController</prop> </props> </property> </bean> <!--控制器配置--> <bean id="methodTestController" class="com.ll19.mvc.method.control.MethodTestController"> <property name="methodNameResolver"> <ref bean="internalPathMethodNameResolver"/> </property> </bean> </beans> |
则在访问updateUser.html的时候会执行MethodTestController.updateUser();,在访问deleteUser.html的时候会执行MethodTestController.deleteUser();。
以下为MethodTestController源码:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
package com.ll19.mvc.method.control;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.multiaction.MultiActionController;
/** * 测试方法名解析器 * * @author <a href="http://www.LL19.com/">LL19.com</a> */ public class MethodTestController extends MultiActionController {
public ModelAndView updateUser(HttpServletRequest request, HttpServletResponse response) throws Exception { System.out.println("updateUser"); return null; }
public ModelAndView deleteUser(HttpServletRequest request, HttpServletResponse response) throws Exception { System.out.println("deleteUser"); return null; } } |
因为只是简单的测试方法名控制器,并没有返回ModelAndView,控制器扩展于MultiActionController。
看扩展的MultiActionController类其实能看出来,也是通过handleRequestInternal来分发给相应的方法,所以你千万不要像其它控制器一向去重写handleRequest。
启动系统后访问deleteUser.html和updateUser.html,分别打印deleteUser和updateUser,测试成功。
其它的方法名解析器汇总,暂时没有特意去测试使用其它两个,感觉另外两个解析器不很实用,对于此类的方法名解析器,用会InternalPathMethodNameResolver一个足矣:
1、InternalPathMethodNameResolver——这是MultiActionController的缺省情况下的方法名解析器,它是根据URL样式来解析方法名的。(实际上就是根据URL中的“文件名部分”决定的)。除了上面的缺省的解析器外,MultiActionController还有两个方法名解析器:
2、ParameterMethodNameResolver——根据请求中的参数来解析并执行方法名。
3、PropertiesMethodNameResolver——根据查询一个key/value列表来解析并执行方法名。
浙公网安备 33010602011771号