Spring学习(十六)-SpringMVC入门

SpringMVC框架围绕DispatcherServlet这个核心展开,DispatcherServlet是SpringMVC的总指挥,它负责截获请求并将其分配给其相应的处理器处理。SpringMVC框架包括注解驱动控制器、请求及响应的信息处理、视图解析、本地化解析、上传文件解析、异常处理以及表单标签绑定等内容。

体系结构

SpringMVC是基于model2实现的技术框架,model2是经典的MVC(model、view、control)模型在Web应用中的变体,这个改变主要源于HTTP协议的无状态性。model2的目的和MVC一样,也是利用处理器分离模型、视图和控制,达到不同技术层级间松散层耦合的效果,提高系统灵活性、复用性和可维护性。

从接收请求到返回响应,SpringMVC框架中各个组件相互配合,各司其职。在整个框架中,DispatcherServlet处于核心位置,它负责协调和组织不同组件来完成请求处理并返回响应的工作。SpringMVC通过一个前端Servlet接收所有的请求,并将具体工作委托给其他组件进行处理,DispatcherServlet就是SpringMVC的前端Servlet。

  1. 从客户端发送一个HTTP请求开始,Web应用服务器接收到这个请求,如果匹配DispatcherServlet的请求映射路径(在web.xml指定),Web容器将该请求转交给DispatcherServlet处理。
  2. DispatcherServlet接收到请求后,将根据请求的信息(URL、HTTP方法、请求报文头、请求参数、Cookie等)及HandlerMapping的配置找到处理请求的处理器(Handler)。
  3. 当DispatcherServlet根据HandlerMapping得到对应当前请求的控制器后,通过HandlerAdapter对Handler进行封装,再以统一的适配器接口调用Handler。HandlerAdapter是SpringMVC的框架级接口,HandlerAdapter是一个适配器,它用统一的接口对各种Handler方法进行调用。
  4. 处理器完成业务逻辑的处理后将返回一个ModelAndView给DispatcherServlet,ModelAndView包含了视图逻辑名和模型数据信息。
  5. ModelAndView中包含的是“逻辑视图名”而非真正的视图对象,DispatcherServlet借由ViewResolver完成逻辑视图名到真实视图对象的解析工作。
  6. 当得到真实视图对象View后,DispatcherServlet就是用这个View对象对ModelAndView中的模型数据进行视图渲染。
  7. 最终客户端得到响应信息,可能是一个普通的HTML页面,也可能是一个XML或JSON串,甚至是一张图片或一个PDF文档等不同的媒体形式。

SpringMVC简单例子

SpringMVC应用一般包括以下步骤:

  1. 配置web.xml,指定业务层对应的Spring配置文件,定义DispatcherServlet;
  2. 编写处理请求的控制器(处理器);
  3. 编写视图对象(JSP等视图对象);
  4. 配置SpringMVC的配置文件,使控制器、视图解析器等生效。

配置XML文件:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
	xmlns="http://java.sun.com/xml/ns/javaee" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
	http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <display-name></display-name>	
  <!-- ①从类路径下加载Spring配置文件,classpath关键字特指类路径下加载 -->
  <context-param>
  	<param-name>contextConfigLocation</param-name>
  	<param-value>
  		classpath:applicationContext.xml
  	</param-value>
  </context-param>
  <!-- 负责启动Spring容器的监听器,它将引用①出的上下文参数获得Spring配置文件地址 -->
  <listener>
  	<listener-class>
  		org.springframework.web.context.ContextLoaderListener
  	</listener-class>
  </listener>
  <!-- Spring MVC的主控Servlet -->
  <servlet>
  	<servlet-name>demo</servlet-name>
  	<servlet-class>
  		org.springframework.web.servlet.DispatcherServlet
  	</servlet-class>
  	<load-on-startup>2</load-on-startup>
  </servlet>
  <!-- Spring MVC处理的URL -->
  <servlet-mapping>
  	<servlet-name>demo</servlet-name>
  	<url-pattern>*.html</url-pattern>
  </servlet-mapping>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>

在XML中声明了一个Servlet,SpringMVC也拥有一个Spring配置文件,该配置文件的文件名和此处定义的Servlet名有一个契约:即采用<Servlet 名>-servlet.xml的形式。在这里,Servlet名为demo,则在/WEB-INF目录下必须提供一个demo-servlet.xml的SpirngMVC配置文件,但这个配置文件无须通过web.xml的ContextLoaderListener上下文参数进行声明,因为SpringMVC的Servlet会自动将demo-servlet.xml和Spring其他配置文件进行拼装。

与此同时,对这个Servlet的URL路径映射进行定义,在这里让所有以.html为后缀的URL都被demo Servlet截获,进而转由SpringMVC框架进行处理。

请求被SpringMVC截获后,首先根据请求的URL查找到目标的处理控制器,并将请求参数封装成一个对象一起传给控制器处理,控制器调用Spring容器中的业务Bean完成业务处理工作并返回结果视图。

编写控制器:

.....
@Controller//标注称为一个Spring MVC的Controller
public class LoginController {
	
	@Autowired
	private UserService userService;
	
	//负责处理index.html的请求
	@RequestMapping(value="/index.html")
	public String loginPage(){
		return "login";
	}
	
	//负责处理loginCheck.html的请求
	@RequestMapping(value="/loginCheck.html")
	public ModelAndView loginCheck(HttpServletRequest request,LoginCommand loginCommand){
		boolean isValidUser = userService.hasMatchUser(loginCommand.getUserName(), loginCommand.getPassword());
		if(!isValidUser){
			return new ModelAndView("login","error","用户名或密码错误。");
		}else{
			User user = userService.findUserByUserName(loginCommand.getUserName());
			user.setLastIp(request.getRemoteAddr());
			user.setLastVisit(new Date());
			userService.loginSuccess(user);
			request.getSession().setAttribute("user", user);
			return new ModelAndView("main");
		}
	}
}

通过SpringMVC的@Controller注解可以将任何一个POJO的类标注为SpringMVC的控制器,处理HTTP的请求。当然标注了@Controller的类首先会是一个Bean,所以我们可以使用@Autowired进行Bean的注入。

一个控制器可以拥有多个对应不同的HTTP请求路径的处理方法,通过@RequestMapping指定方法如何映射请求路径。

请求的参数会根据参数名称默认契约自动绑定到响应方法的入参中,在loginCheck(HttpServletRequest request,LoginCommand loginCommand)方法中,请求参数会按名称匹配绑定到loginCommand的入参中。

请求响应方法可以返回一个ModelAndView,或直接返回一个字符串,SpringMVC会解析之并转向目标响应页面。ModelAndView对象既包括了视图信息又包括了视图渲染所需的模型数据信息。控制器根据登录处理结果分别返回ModelAndView(“login”,“error”,“用户名或密码错误。”)和ModelAndView(“main”)。ModelAndView的第一个参数代表视图的逻辑名,第二和第三个参数分别为数据模型名称和数据模型对象,数据模型对象将以数据模型名称为参数名放置到request的属性中。

编写视图对象:

<%@ 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 PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>登录首页</title>
</head>
<body>
	<c:if test="${!empty error}">
		<font color="red"><c:out value="${error}"/></font>
	</c:if>
	<form action="<c:url value="/loginCheck.html"/>" method="post">
		用户名:<input type="text" name="userName"><br>
		密码:<input type="password" name="password"><br>
		<input type="submit" value="登录"/>
		<input type="reset" value="重置"/>
	</form>
</body>
</html>

login.jsp页面有两个用处,既作为登录页面又作为登录失败后的响应页面。所以定义了<c:if test="${!empty error}">,使用JSTL标签将登录错误返回的信息进行处理。JSTL标签中引用了error变量,这变量正是LoginController中返回的ModelAndView(“login”,“error”,“用户名或密码错误。”)对象所声明的error参数。其中,userName和password应该与实体类LoginCommand对应参数,这样才可以将请求参数按匹配入参。

login.jsp的登录表单提交到loginCheck.html,<c:url value="/loginCheck.html"/>的JSTL标签会在URL前自动加上应用程序部署根目录。

由于login.jsp放置在WEB-INF/jsp目录下,无法直接通过URL进行调用,它由LoginController控制类中标注了@RequestMapping(value="/index.html")的loginPage()进行转发。

配置SpringMVC文件:

<?xml version="1.0" encoding="UTF-8"?>
<!-- 引用Spring的多个Schema空间的格式定义文件 -->
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xmlns:cache="http://www.springframework.org/schema/cache"
    xsi:schemaLocation="
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd
    http://www.springframework.org/schema/jdbc
    http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
    http://www.springframework.org/schema/cache
    http://www.springframework.org/schema/cache/spring-cache-3.1.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/util
    http://www.springframework.org/schema/util/spring-util.xsd">
    
    <!-- 扫描web包,应用Spring的注解 -->
    <context:component-scan base-package="web"/>
    <!-- 配置视图解析器,将ModelAndView及字符串解析为具体的页面 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
    	p:viewClass="org.springframework.web.servlet.view.JstlView"
    	p:prefix="/WEB-INF/jsp/"
    	p:suffix=".jsp"/>
</beans>

我们需要在demo-servlet.xml中声明该控制器,扫描Web路径,指定SpringMVC的视图解析器。通过InternalResourceViewResolver为视图逻辑名添加前后缀的方式进行解析。如视图逻辑名为“login”将解析为/WEB-INF/jsp/login.jsp,名为“main”的视图解析为/WEB-INF/jsp/main.jsp。

整个例子实现的过程:

  1. DispatcherServlet接收到客户端的/loginCheck.html请求;
  2. DispatcherServlet使用DefaultAnnotationHandlerMapping查找负责处理该请求的控制器为“/loginCheck.html”;
  3. DispatcherServlet将请求分发给名为“/loginCheck.html”的LoginController处理器;
  4. 处理器完成业务处理后,返回ModelAndView对象,其中View的逻辑名为“main”或“login”;
  5. DispatcherServlet调用InternalResourceViewResolver组件对ModelAndView中的逻辑视图名进行解析,得到真实的“/WEB-INF/jsp/login.jsp”或“/WEB-INF/jsp/main.jsp”视图对象;
  6. DispatcherServlet使用真实的视图对象对模型中的数据信息进行渲染;
  7. 返回响应页面给客户端。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值