超详细SpringMVC学习笔记,深入底层

本文详细介绍了SpringMVC的各个方面,从概述、环境搭建、前端控制器DispatcherServlet,到视图解析器、页面控制器处理、RESTful风格、拦截器HandlerInterceptor、文件上传下载以及异常处理。讲解了请求响应流程,包括@RequestMapping注解、模型数据的传递与响应,以及如何使用json。此外,还探讨了SpringMVC中自定义拦截器的实现和文件上传下载的处理方法,最后总结了SpringMVC的工作流程,是深入学习SpringMVC的重要参考资料。
摘要由CSDN通过智能技术生成

文章目录

一、SpringMVC概述

Spring MVC一种开源的、轻量级的、基于MVC的Web层应用框架。偏前端而不是基于业务逻辑层。

​ SpringMVC是Spring中的模块,它实现了mvc设计模式的web框架,Spring web MVC框架提供了MVC(模型 - 视图 - 控制器)架构,用于开发灵活和松散耦合的Web应用程序的组件。 MVC模式使应用程序的不同组件(输入逻辑,业务逻辑和UI逻辑)合理有效的分离,同时又有效的将各组件组合一起完成功能。

· 模型(Model) 封装了应用程序数据,通常它们将由POJO类组成。

· 视图(View) 负责渲染模型数据,一般来说它负责生成客户端浏览器可以解释HTML输出。

· 控制器(Controller) 负责处理用户请求并构建适当的模型,并将其传递给视图进行渲染。

常用组件:

DispatcherServlet:前端控制器

Controller:处理器/页面控制器,做的是MVC中的C的事情,但控制逻辑转移到前端控制器了,用于对请求进行处理

HandlerMapping :请求映射到处理器,如果映射成功返回一个HandlerExecutiongChain对象(包含一个Handler处理器(页面控制器)对象、多个HandlerInterceptor拦截器对象)

ViewResolver : 视图解析器,找谁来处理返回的页面。把逻辑视图解析为具体的View,进行这种策略模式,很容易更换其他视图技术;如InternalResourceViewResolver将逻辑视图名映射为JSP视图

LocalResolver:本地化、国际化

MultipartResolver:文件上传解析器

HandlerExceptionResolver:异常处理器

二、环境搭建

2.1 HelloWorld范例

①新建Web工程,加入 jar 包;

②配置web.xml:

<!-- 配置SpringMVC核心控制器:DispatcherServlet -->
<!--前端控制器 dispatcherServlet  其实就是servlet-->
    <servlet>
        <!--servlet的友好名称,建议跟类名保持一致-->
        <servlet-name>dispatcherServlet</servlet-name>
        <!--servlet的全类名-->
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--servlet实例创建并且读取springmvc配置文件-->
        <init-param>
            <!--DispatcherServlet类中的一个属性:contextConfigLocation,里面配置springmvc的路径-->
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <!-- 表示前端控制器在服务器启动时就加载servlet实例 -->
        <load-on-startup>1</load-on-startup>
    </servlet>


    <!-- 配置前端控制器的访问地址 -->
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <!--
        	/和/*的区别
        	/ 如果请求访问的是jsp,则不拦截
        	/* 全部拦截,包含jsp页面
        -->
        <url-pattern>/</url-pattern>
    </servlet-mapping>

③配置Springmvc.xml:

<!-- 包扫描 -->
<context:component-scan base-package="Controller"></context:component-scan>
    <!-- 
		配置映射解析器:
         拼接完整的页面路径信息
         /WEB-INF/views/+逻辑名字+.jsp
         /WEB-INF/views/welcome.jsp
    -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- prefix:逻辑名字前的路径信息 -->
        <property name="prefix" value="/WEB-INF/views/"></property>
        <!-- suffix:逻辑名字页面的后缀 -->
        <property name="suffix" value=".jsp"></property>
    </bean>

	<!-- 如果将DispatcherServlet请求映射配置为"/",则Spring MVC将捕获Web容器所有的请求,包括静态资源的请求,Spring MVC会将它们当成一个普通请求处理,因此找不到对应处理器将导致错误。 -->
	<!-- 在springMVC-servlet.xml中配置<mvc:default-servlet-handler />后,会在Spring MVC上下文中定义一个DefaultServletHttpRequestHandler,它会像一个检查员,对进入DispatcherServlet的URL进行筛查,如果是静态资源的请求,就将该请求转由Web应用服务器默认的Servlet处理,如果不是静态资源的请求,才由DispatcherServlet继续处理。 -->
	<mvc:default-servlet-handler></mvc:default-servlet-handler>

	<!-- <mvc:annotation-driven /> 会自动注册:RequestMappingHandlerMapping 、RequestMappingHandlerAdapter 与 ExceptionHandlerExceptionResolver  三个bean。-->
<!--
		支持使用 ConversionService 实例对表单参数进行类型转换
		支持使用 @NumberFormat、@DateTimeFormat 注解完成数据类型的格式化
		支持使用 @Valid 注解对 JavaBean 实例进行 JSR 303 验证
		支持使用 @RequestBody 和 @ResponseBody 注解
-->
    <mvc:annotation-driven></mvc:annotation-driven>

④Controlller控制器类:

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;


//@Controller 注解是把HelloController类配置到Spring容器中

@Controller
public class HelloController {
   
    /**
     * 使用 @RequestMapping 注解来映射请求的 URL
     * / : 在web工程中表示路径为:http://ip:port/工程路径
     * /hello : 表示地址为:http://ip:port/工程路径/hello
     */
    @RequestMapping(value = "/hello")
    public String hello(){
   
        System.out.println(" springmvc hello world程序 ");

   /**
    * 返回值会通过视图解析器解析为实际的物理视图, 
    * 对于 InternalResourceViewResolver 视图解析器,会做如下的解析:
    * 通过 prefix + returnVal + suffix 这样的方式得到实际的物理视图,
    * 默认做转发操作.
    * /WEB-INF/views/success.jsp
    */
    
        return "success";
    }
}

⑤视图配置:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
	<h4>helloworld</h4>
</body>
</html>

2.2 HelloWorld流程图解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ASQeqc00-1603766524449)(C:\Users\77161\Desktop\ssm-img\SpringMVC-helloworld流程图解.jpg)]

2.3 请求相应流程图

基本步骤:

  1. 客户端请求提交到DispatcherServlet
  2. 由DispatcherServlet控制器查询一个或多个HandlerMapping,找到处理请求的Controller;
  3. DispatcherServlet将请求提交到Controller(也称为Handler);
  4. Controller调用业务逻辑处理后,返回ModelAndView
  5. DispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图;
  6. 视图负责将结果显示到客户端;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VozAY7gI-1603766524451)(C:\Users\77161\Desktop\ssm-img\流程图.jpg)]

三、 前端控制器

​ DispatcherServlet 是前端控制器设计模式的实现,提供 Spring Web MVC 的集中访问点,并把请求分发到不同的控制器去处理,根据控制器处理后的结果,生成相应的响应发送到客户端。前端控制器既可以使用Filter实现(Struts2采用这种方式),也可以使用Servlet来实现(spring MVC框架)。

3.1 Servlet回顾

SpringMVC是用Servlet来实现前端控制器,所以首先来回忆一下servlet的相关知识。

Servlet生命周期的三个阶段:

在Servlet容器启动后,客户首次向Servlet发送请求,Servlet容器创会建一个Servlet实例。

  1. 初始化:实例化之后紧接着执行初始化,只执行一次,调用init()方法。
  2. 服务:处理请求并响应浏览器,每次从浏览器发送请求访问此servlet,都会调用service()方法。
  3. 销毁:服务器关闭时执行销毁的方法,只执行一次,调用destroy()方法

提示:在web.xml里面的<servlet>标签中,加入<load-on-startup>,就可以将servlet的加载时间提前到服务器启动时,只能设置正整数,负整数或0没有任何效果,而且值越小越先加载。

3.2 DispatcherServlet前端控制器

前端控制器源码分析:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
   
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;
 
 
		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
 
 
		try {
   
			ModelAndView mv = null;
			Exception dispatchException = null;
 
 
			try {
   
				processedRequest = checkMultipart(request);
				multipartRequestParsed = processedRequest != request;
 
 
				// 确定当前请求的处理程序。
				mappedHandler = getHandler(processedRequest, false);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
   
					noHandlerFound(processedRequest, response);
					return;
				}
 
 
				// 确定当前请求的处理程序适配器。
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
 
 
				// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
   
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (logger.isDebugEnabled()) {
   
						String requestUri = urlPathHelper.getRequestUri(request);
						logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
					}
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
   
						return;
					}
				}
 
 
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
   
					return;
				}
 
 
				try {
   
					// Actually invoke the handler.
					mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
				}
				finally {
   
					if (asyncManager.isConcurrentHandlingStarted()) {
   
						return;
					}
				}
 
 
				applyDefaultViewName(request, mv);
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
   
				dispatchException = ex;
			}
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
   
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Error err) {
   
			triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
		}
		finally {
   
			if (asyncManager.isConcurrentHandlingStarted()) {
   
				// Instead of postHandle and afterCompletion
				mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				return;
			}
			// Clean up any resources used by a multipart request.
			if (multipartRequestParsed) {
   
				cleanupMultipart(processedRequest);
			}
		}
	}

第一步:用户发送请求至前端控制器DispatcherServlet;

.action类型的URL通过过滤器进入DispatcherServlet类,调用其doDiapatch()方法:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
   
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;
 
 
		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
                ......
}

第二步:前端控制器调用HandlerMapping处理器映射器,请求获取Handle;

在doDiapatch()方法中调用了DispatcherServlet类的getHandler方法:

HandlerExecutionChain mappedHandler = null;
......
//调用getHandler() 获取HandlerExecutionChain:当前请求地址对应的处理器(链),即请求处理器。
mappedHandler = getHandler(processedRequest, false);

其中getHandler方法:

@Deprecated
	protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {
   
		return getHandler(request);
	}
 
 //在getHandler()方法中遍历了已有的handlerMapping,然后调用handlerMapping.getHandler(request)获得HandlerExecutionChain,而HandlerExecutionChain中包含了需要调度的指定handler。
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   
    	// HandlerMapping: 存储了所有的请求地址和处理器(方法)的对应信息,在组件初始化的时候就存放了所有URL和处理器(方法)的对应信息。
		for (HandlerMapping hm : this.handlerMappings) {
   
			if (logger.isTraceEnabled()) {
   //logger记录器,记录日志信息
				logger.trace(
						"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
			}
             //HandlerExecutionChain:当前请求地址对应的处理器(链)
			HandlerExecutionChain handler = hm.getHandler(request);
			if (handler != null) {
   
				return handler;//说明前端传过来的URL和注解上的路径匹配, 就返回对应的控制器
			}
		}
		return null;
	}

​ 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成),返回一个执行器的链(HandlerExecutionChain)给DispatcherServlet前端控制器;

第三步:回到doDiapatch()方法,调用处理器适配器HandlerAdapter执行Handler,确定方法运行时的参数,利用反射执行目标方法得到执行结果ModelAndView。

ModelAndView mv = null;
......
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
......
//确定方法运行时的参数,利用反射执行目标方法。
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

四、视图解析器

​ 请求处理方法执行完成后,最终返回一个 ModelAndView 对象。对于那些返回 String,View 或 ModeMap 等类型的处理方法,Spring MVC 也会在内部将它们装配成一个 ModelAndView 对象,它包含了逻辑名和模型对象的视图。

​ Spring MVC 借助视图解析器ViewResolver)得到最终的视图对象(View),对于最终究竟采取何种视图对象对模型数据进行渲染,处理器并不关心,处理器工作重点聚焦在生产模型数据的工作上,从而实现 MVC 的充分解耦。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HSgVlHIq-1603766524453)(C:\Users\77161\Desktop\ssm-img\视图解析器.jpg)]

4.1 视图

​ 视图的作用是渲染模型数据,将模型里的数据以某种形式呈现给客户。为了实现视图模型和具体实现技术的解耦,Spring 在 org.springframework.web.servlet 包中定义了一个高度抽象的 View 接口:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h9oatUf1-1603766524455)(C:\Users\77161\Desktop\ssm-img\view接口.jpg)]

  • 视图对象由视图解析器负责实例化。由于视图是无状态的,所以他们不会有线程安全的问题。

常用视图实现类:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2gyNyeE0-1603766524456)(C:\Users\77161\Desktop\ssm-img\视图实现类.jpg)]

4.2 ViewResolve视图解析器

​ SpringMVC 为逻辑视图名的解析提供了不同的策略,可以在 Spring WEB 上下文中配置一种或多种解析策略并指定他们之间的先后顺序。每一种映射策略对应一个具体的视图解析器实现类。视图解析器的作用比较单一:将逻辑视图解析为一个具体的视图对象。

所有的视图解析器都必须实现ViewResolver接口:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值