《第7章 使用Spring MVC构造Web应用程序》
Spring MVC 基于模型——视图——控制器(Model-View-Contrlloer,MVC)模式实现
可以帮助我们构建像Spring那样灵活和松耦合的Web应用程序
Spring MVC 起步
1.跟踪Spring的请求
当用户在Web浏览器中点击链接或提交表单的时候,请求工作就开始了
请求是一个十分繁忙的家伙,从离开浏览器开始到获取响应返回
它会经历好多站,在站都会留下一些信息同时也会带上其它信息
请求会由DispatchServle,又称为前端控制器,先发送给处理器映射
根据处理器映射可以知道该请求应该被分配给哪个控制器
在控制器完成处理后,请求会被发送给一个视图(根据视图解析器)来呈现输出结果
2.搭建Spring MVC
Spring MVC的核心是DispatchServlet
它充当Spring MVC的前端控制器
DispatchServlet必须在Web程序的web.xml配置
故需先将<servlet>声明放入web.xml中:
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet=name>元素比较重要
默认情况下,DispatchServlet在加载时会从一个基于这个Sertlet名字的XML文件中加载Spring应用上下文
在这个示例中,Servlet的名字为dispatch
因此DispatchServlet将会尝试从一个名为dispatch-servlet.xml来加载应用上下文
换句话说,DispatchServlet将使用dispatch-servlet.xml来创建应用上下文
接下来我们必须声明DispatchServlet处理哪些URL
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.form</url-pattern>
</servlet-mapping>
其中<servlet-mapping>标签声明了与servlet相应的匹配规则
每个<url-pattern>标签代表1个匹配规则
这里*.form是匹配指任何以form作为后缀名的文件
编写基本的控制器
在这个环节,我们应该为应用程序所提供的每一种资源编写一个单独的控制器
而不是为每个用例编写一个控制器,一般地我们使用注解来配置控制器
DispatchServlet需要咨询一个或多个处理器映射来明确地将请求分发给哪个控制器
Spring自带了多个处理器映射实现供我们实现
- BeanNameUrlHandlerMapping
- 根据控制器Bean的名字将控制器映射到URL
- ControllerBeanNameHandlerMapping
- 根据控制器Bean的名字将控制器映射到URL,Bean的名字不遵循URL的约定
- DefaultAnnotationHandlerMapping
- 将请求映射给使用@RequestMapping注解的控制器和方法
- SimpleUrlHandlerMapping
- 使用定义在Spring应用上下文的属性集合将控制器映射到URL
虽然提供了很多用于处理器映射的实现,但实际上主要使用的还是基于注解的控制器类
因此,DispatchServlet所提供的DefaultAnnotationHandlerMapping基本上能满足我们的需求
DefaultAnnotationHandlerMapping能将请求映射到使用@RequestMapping注解的方法
但是实现注解驱动的Spring MVC并不仅仅是将请求映射到控制器的方法
在构建控制器时,我们还需要使用注解将请求参数绑定到控制器的方法参数上进行校验以及信息转换
所以还需要Spring MVC所提供的注解驱动特性
<mvc:annotation-driven/>
定义一个简单的控制器
@Controller
//声明该类为控制器类
public class HelloMVCController {
//用于建立请求URL和处理方法之间的对应关系,即对hello请求执行sayHello方法
@RequestMapping(value="/hello",method = RequestMethod.GET)
public String sayHello(){
System.out.println("hello world");
//返回视图的名称
return "success";
}
}
解析视图
处理请求的最有一件事必须要做的事情是为用户渲染输出
这个任务落在了视图实现上——通常是JSP
为了确定指定的请求需要使用哪个视图,DispatchServlet会查找一个视图解析器
根据视图解析器来将控制器返回的逻辑视图名称转换成渲染结果的实际视图
其实,视图解析器所做的就是将视图名称与JSP进行匹配
Spring提供的视图解析器很多,但主要介绍了InternalResourceViewResolver视图解析器
在Spring MVC中,大量使用了约定优于配置的开发模式
InternalResourceViewResolver就是一个面向约定的元素
它将逻辑视图名称解析为View对象,而该对象将渲染的任务委托给Web应用程序上下文的一个模板(JSP)
InternalResourceViewResolver通过为逻辑视图名称添加特定的前缀和后缀来得到视图模板的路径
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/pages/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
当DispatchServlet要求InternalResourceViewResolver解析视图时,它将获得一个逻辑视图名称
如上述的"success",然后添加前缀"/WEB-INF/pages/"和后缀".jsp"
得到的结果/WEB-INF/pages/sucess.jsp就是渲染输出的JSP路径
完成Spring应用上下文
正如前面所说的,DispatchServlet会根据一个XML文件来加载其Spring应用上下文
而这个文件的名字基于它的<servlet-name>属性来确定
但若是其它类的Bean怎么办呢?也声明在dispatch-servlet.xml文件中吗?
但实际上我们不会这样做,更多的是将Spring配置文件组织到多个文件中
比如一个配置文件用于服务层,一个用于持久层,一个用于数据源之类的
基于这样的理念,将Web层的配置都放在dispatch-servlet.xml文件是在情理之中
这个文件会被DispatchServlet加载,但我们还需要另外一种方式来加载其他的配置
这就是ContextLoaderListener能够发挥作用的地方了
ContextLoaderListener是一个Servlet监听器,除了DispatchServlet创建的应用上下文外,它能够加载其他的配置文件
到一个Spring应用上下文中
为了使用ContextLoaderListener,需要在web.xml文件中添加如下的<listener>声明
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
我们必须要告诉ContextLoaderListener需要加载哪些配置文件
如果没有指定,上下文加载器会查找/WEB-INF/applicationContext.xml这个Spring配置文件
但这个文件本身并没有做到将应用上下文拆分为多个片段,所以需要重写默认实现
为了给ContextLoaderListener指定一个或者多个Spring配置文件
需要在servlet上下文中配置contextConfigLocation参数
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
<!--默认实现-->
/WEB-INF/applicationContext.xml
<!--重写默认实现-->
/WEB-INF/***.xml
classpath:service-context.xml
classpath:persistence-context.xml
classpath:dataSource-context.xml
</param-value>
</context-param>
contextConfigLocation参数指定了一个路径的列表
除非特别声明,路径是相对于应用程序根目录的
在这里,Spring配置被分成了多个XML文件