Spring MVC工作流与配置-部署Tomcat服务器上运行

当初学习Spring MVC时,最吸引我的是它如何降低整个程序各块组件之间的耦合度,以及各组件之间的通信流程。降低耦合度的优点无疑就是方便了以后对代码进行扩展,维护,让各个组件独立开来,又能清晰地互相协调通信,是给我感受最深的Spring MVC的优点。使用Spring MVC的Web项目,当用户发送一个HTTP请求后,数据经过哪些处理,如何处理以及如何显示到view上,下面就来看一下简单的配置项目,看看它有哪些组件,以及如何一步一步进行各组件之间的协同工作的。

 

Spring MVC工作流

先来看看Spring MVC收到一个HTTP请求后的工作流程,看看它都用到了哪些API,当你搞懂了整个工作流程后,才能明白各组件之间的关系!

由上面的流程图可以看到,对于一个用户发送的url请求:

  1. 该请求首先会由前端控制器DispatchServlet(或者说是分发器Servlet)接收。
  2. 前端控制器会对处理器映射器HandlerMapping发送请求,在处理器映射器中查找可以处理该请求的Handler。处理器映射器的作用就是根据url路径,去查找相应的处理器Handler。
  3. 前端控制器得到处理器映射器返回的执行链后,再去请求处理器适配器HandlerAdapter,根据执行链去执行相应的Handler。
  4. 处理器Handler执行完成后,向处理器适配器返回一个ModelAndView对象,里面封装了数据模型和视图信息。处理器适配器接收到ModelAndView对象后,再把它返回给前端控制器。
  5. 前端控制器收到ModelAndView对象后,去请求视图解析器View resolver,视图解析器的作用是将ModelAndView对象中的View信息(View是一个接口,它可以有不同的实现,如jsp、excel,pdf等),解析得到一个View页面,例如把View里面的JSP路径信息解析返回一个JSP页面。最后把View页面返回给前端控制器。
  6. 最后,前端控制器将具体视图View进行渲染,把Model里面的数据填充到View页面中,得到最终要返回给用户看的视图。
  7. 前端控制器向用户发送回应请求。

上面就是Spring MVC在接收到用户发送的请求后,处理的过程,可以看到,里面使用到的组件有前端控制器、处理器映射器、处理器适配器、处理器与控制器,视图解析器和视图。下面来总结一下这些组件以及一些定义的接口。

 

组件及接口

前端控制器DispatcherServlet

第一个,也是十分重要的一个,前端控制器DispatcherServlet,它是一个实现类,作用可以说是一个转发器,负责对各个组件进行统一调度。由上面的工作流程可以看到,DispatcherServlet接收用户HTTP请求,根据在处理器映射器中找到的Handler,到处理器适配器中执行,处理器Handler进行完业务逻辑处理后,返回一个ModelAndView对象,里面包含了Model数据模型和View视图信息。最后DispatcherServlet请求视图解析器View solver解析得到真正的视图,并把视图渲染,填入数据域,最终返回给用户。

可以看到,前端控制器DisparcherServlet能找到各个组件,进行统一通信,管理,降低了各组件之间的耦合度。

处理器映射器HandlerMapping

就像MyBatis里的SQL映射配置resultMap一样,处理器映射器也是可以有多个,处理器映射器的作用就是根据HTTP请求(HttpServletRequest),作为参数传入到getHandler()方法中,该方法返回一个处理器执行链,我们就能根据这个执行链得到匹配的处理器。

处理器适配器HandlerAdapter

处理器适配器负责根据映射器得到的执行链,判断该执行器是否支持,通常一个执行器Handler会对应处理器适配器的一个实现方法,处理器适配器通过supports()方法传入适配器信息,判断是否支持该适配器。若支持,则可以调用处理器Handler中的handle()方法,传入参数HttpServletRequest和HttpServletResponse。

处理器与控制器Handler

处理器Handler是进行业务逻辑处理的部分,十分重要,请求经过一层一层传递,最后到达可以执行数据处理的地方。处理器提供一个接口handleRequest(),需要两个参数,HTTP请求(HttpServletRequest)和HTTP响应(HttpServletResponse),并返回一个ModelAndView对象。方法中进行数据模型的处理(Spring MVC中的数据模型通常是Map数据结构)和视图处理(指定一个视图路径)。然后把带有数据模型和逻辑视图信息的ModelAndView对象返回给处理器适配器,最后有处理器适配器返回给DispatcherServlet。

视图解析器View resolver

因为ModelAndView对象中的视图信息是一个逻辑视图名,如JSP页面的路径,所以要通过View resolver根据路径得到真正的视图。

视图渲染View

在得到真正的视图View后(如一个JSP页面),然后就是视图渲染部分,把ModelAndView中的数据填充到视图中,生成最终给用户的视图。

环境配置和测试用例

前端控制器-拦截配置

就像前面MyBatis和Spring整合的配置中,数据源配置交由了Spring管理,所以数据源信息bean放到了Spring的配置文件中,还有SqlSessionFactory也被Spring采用单例形式管理,所以会话工厂的信息也被配置到了Spring中。

同样的,用户的HTTP请求想要交由Spring MVC处理,要配置拦截信息,拦截用户的请求,交由DispatcherServlet处理。

<!-- Spring MVC前端控制器 -->
	<servlet>
	<!-- 定义servlet配置,实现类为DispatcherServlet,即前端控制器类 -->
		<servlet-name>springmvc</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<!-- DispatcherServlet初始化参数,加载的配置文件为编译目录classpath -->
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:springmvc.xml</param-value>
		</init-param>
	</servlet>
		
	<servlet-mapping>
		<servlet-name>springmvc</servlet-name>
		<url-pattern>*.action</url-pattern>
	</servlet-mapping>

在servlet标签对中,我们配置一个名为springmvc的servlet配置,实现类为我们的前端控制器DispatcherServlet类。下一个标签对servlet-mapping十分重要,它就是用来拦截用户的请求,url-pattern标签对配置只要符合任意字符加“.action”形式的url请求,就会为该请求映射一个名为“springmvc”的servlet配置,“springmvc”就是我们上面配置的DispatcherServlet,Spring MVC的前端控制器。这样来完成对用户请求的拦截,接下来都会交由DispatcherServlet去处理这个请求。

Spring配置文件

与前面MyBatis整合Spring时相同,Spring配置文件在beans标签对的头部配置xmlns信息,和模式文档xsi信息。不同的地方在于下面的配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
		http://www.springframework.org/schema/mvc
		http://www.springframework.org/schema/mvc/spring-mvc-3.2xsd
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context-3.2.xsd
		http://www.springframework.org/schema/aop
		http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
		http://www.springframework.org/schema/tx
		http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
		
		<!-- 处理器映射器 -->
		<!-- 将bean的name作为url进行查找 -->
		<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
		
		<!-- 处理器适配器HandlerAdapter -->
		<!-- 使用SimpleControllerHandlerAdapter,因为其支持所有实现了Controller接口的Handler控制器 -->
		<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
		
		<!-- 视图解析器ViewResolver -->
		<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" />
			
		<!-- 配置一个可以被url映射的Handler -->
		<bean name="/queryUsersTest.action" class="com.mvc.controller.UserControllerTest" />
		
</beans>

正如我们前面所看到,接收到用户的HTTP请求后,前端控制器DispatcherServlet会去请求处理器映射器HandlerMapping,查找Handler,所以第21行我们要配置一个处理器映射器。处理器映射器有很多种,例如:

  1. BeanNameUrlHandlMapping:Bean名称URL处理器映射。以URL为名称来声明的Bean处理器,然后使用在Bean名称中声明的URL和请求的URL进行匹配。
  2. DefaultAnnotationHandlerMapping:默认注解处理器映射。通过请求映射注解(@RequestMapping)来注册处理器映射,注解中包含了匹配请求的URL。
  3. ControllerClassNameHandlerMapping:控制器类名处理器映射。转换规则是,把“.”分隔的带有包名前缀的类名替换成以斜线“/”分隔的带有包名前缀的字符串,再加上前缀和后缀构成URL。
  4. ControllerBeanNameHandlerMapping:控制器Bean名称处理器映射。根据控制器的Bean名称转换,即把Bean名称加上前缀和后缀构成URL。
  5. SimpleUrlHandlerMapping:简单URL处理器映射。自定义配置从URL到处理器的映射。

这里我们选择BeanNameUrlHandlerMapping类,也就是映射规则为将bean的name属性作为url进行查找。

      处理器映射器返回一个执行链后,DispatcherServlet就会带着它去请求处理器适配器HandlerAdapter,所以下面第25行我们配置一个处理器适配器,处理器适配器同样有多种,例如简单控制器处理器适配器、注解方式的适配器、HTTP请求适配器和简单Servlet

适配器。这里我们使用简单控制器处理器SimpleControllerHandlerAdapter,它支持那些实现了Controller接口的Handler控制器类,对于业务逻辑处理的Handler类,正是我们要重点编写的。

      接下来第31行就是配置我们的Handler了,name属性供处理器映射器查找,class属性则是处理器Handler的实现类。

      最后,处理器完成业务逻辑,会返回一个ModelAndView对象,里面包含了需要跳转的视图信息View和需要在视图中显示的数据Model,此时需要视图解析器View resolver去解析ModelAndView对象。第28行配置我们的视图解析器,分类有InternalResourceViewResolver(根据模板名和位置解析)、XMLViewResolver(根据xml配置文件解析)和ResourceBundleViewResolver(根据properties资源集解析)。这里我们选择根据模板名和位置进行解析,它会根据ModelAndView对象中的视图路径来加载视图,下面在Handler实现类中我们会看到。

Handler处理器实现类

在前面的DispatcherServlet、HandlerMapping和HandlerAdapter都配置好后,来到了关键的一步,对请求进行业务逻辑处理的Handler处理器实现类编写:

// User Handler处理器
public class UserControllerTest implements Controller {
	private UserService userService = new UserService();

	@Override
	public ModelAndView handleRequest(HttpServletRequest request, 
			HttpServletResponse response) throws Exception {
		// 数据模型Model,模拟获取用户信息列表
		List<User> userList = userService.queryUserList();
		// 返回的ModelAndView对象
		ModelAndView modelAndView = new ModelAndView();
		modelAndView.addObject("userList", userList);
		// 指定视图View
		modelAndView.setViewName("/WEB-INF/jsp/users/userList.jsp");
		
		// 数据处理完后,返回视图和数据
		return modelAndView;
	}
	
	// 模拟Service内部类
	class UserService {
		public List<User> queryUserList() {
			List<User> userList = new ArrayList<User>();
			
			User user1 = new User();
			user1.setUsername("理莎");
			user1.setEmail("[email protected]");
			user1.setGender("女");
			user1.setProvince("广东省");
			user1.setCity("广州市");
			
			User user2 = new User();
			user2.setUsername("张三");
			user2.setEmail("[email protected]");
			user2.setGender("男");
			user2.setProvince("山西省");
			user2.setCity("太原市");
			
			User user3 = new User();
			user3.setUsername("赵敏");
			user3.setEmail("[email protected]");
			user3.setGender("女");
			user3.setProvince("江苏省");
			user3.setCity("南京市");
			
			userList.add(user1);
			userList.add(user2);
			userList.add(user3);
			
			return userList;
		}
	}
}

编写处理器实现类,首先要实现Controller接口,因为我们前面配置的处理器适配器是SimpleControllerHandlerAdapter,支持该适配器的Handler都要实现Controller接口。接着重写接口里的handleRequest()方法,在方法中进行业务逻辑处理。假设请求是显示数据表内的注册用户信息,我们用集合List作为数据模型,存放用户信息,调用UserService类中的queryUserList()方法模拟查询用户信息(因为这篇日志是讲Spring MVC的,我暂时不把MyBatis整合进去,免得工程文件太多,大家专注于Spring MVC即可,往后我会写整合MyBatis的)。第21行初始化一个ModelAndView对象,把数据模型userList放进去,然后设置逻辑视图,即视图路径,最后返回这个对象,业务逻辑完成。

视图渲染

在业务逻辑处理中,我们的视图是jsp页面,最终视图渲染部分,我们要把数据填充到页面中,下面来看看jsp视图部分:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>用户列表</title>
</head>
<body>
	<h1>注册用户</h1>
	<table  border=1>
		<tr>
			<td>顾客名</td>
			<td>性别</td>
			<td>电子邮箱</td>
			<td>省会</td>
			<td>城市</td>
		</tr>
		<!-- 使用JSTL的c标签来遍历服务端的用户表数据userList -->
		<c:forEach items="${userList }" var="user">
			<tr>
				<td>${user.username }</td>
				<td>${user.gender }</td>
				<td>${user.email }</td>
				<td>${user.province }</td>
				<td>${user.city }</td>
			</tr>
		</c:forEach>
	</table>
</body>
</html>

在前端页面中做视图渲染,也就是把数据填充进去。我们遍历ModelAndView中的数据模型userList,读出里面的信息并显示到列表上。在部署完Tomcat服务器后,打开浏览器访问路径:

http://localhost:8080/SpringMVC__Project/queryUsersTest.action

就可以看到处理信息:

 

完整实现代码已上传GitHub:

https://github.com/justinzengtm/SSM-Framework/tree/master/SpringMVC_Project

发布了97 篇原创文章 · 获赞 71 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/justinzengTM/article/details/101609707
今日推荐