SpringMVC应用及源码分析

第一部分 SpringMVC应用

1. SpringMVC简介

1.1 MVC结构体系

三层架构

当今互联网开发架构一般是基于两种形式,一种是C/S(客户端/服务端);另一种是B/S架构(浏览器/服务端)。在JavaEE开发中,几乎都是基于B/S架构开发。在B/S架构中,系统又分为三层架构,包括:表现层、业务层、持久层。

  • 表现层

也就是常说的web层,它负责接收客户端请求,向客户端响应结构,通常使用http协议,web层接收http请求,完成http响应。

表现层包括:展示层和控制层,控制层负责接收请求,展示层负责结果展示。

  • 业务层

也就是常说的service层,它负责处理业务逻辑,和我们开发项目的需求息息相关。

表现层和业务层直接的关系:表现层依赖业务层,业务层不依赖表现层。

  • 持久层

也就是常说的dao层,负责数据持久化,包括数据层,即数据库和数据访问层,数据库是对数据进行持久化的载体,数据访问层是业务层和持久层交互的接口,通俗来讲就是持久层和数据库进行交互,对数据库表进行增删改查。

MVC设计模型

MVC,即模型(model) - 视图(view) - 控制器(controller)的缩写。

  • Model:包含业务模型和数据模型,数据模型用于封装数据,业务模型用于处理业务。
  • View:指Jsp或html,用于展示数据。
  • Controller:web应用程序中处理用户交互,处理程序逻辑。

1.2 SpringMVC是什么?

SpringMVC全称Spring Web MVC,是基于MVC设计模型的请求驱动类型的轻量级Web框架,属于SpringFrameWork后续产品。

在这里插入图片描述

SpringMVC已成为目前最主流的MVC框架之一,已全面超越Struts2,成为最有效的MVC框架。

它本质上是对Servlet的封装,简化了servlet的开发。

作用:1)接收请求;2)返回响应,跳转页面。

主要职责:处理前端http请求。

2. SpringMVC工作流程

在这里插入图片描述

流程说明

  • 用户发起请求至前端控制器DispatcherServlet
  • DispatcherServlet接收请求调用HandlerMapping处理器映射器
  • HandlerMapping处理器映射器根据请求url找到具体的Handler(后端控制器),生成处理器对象及处理器拦截器一并返回DispatcherServlet
  • DispatcherServlet调用HandlerAdapter处理器适配器去调用Handler
  • 处理器适配器执行Handler,执行完成后返回ModelAndView至DispatcherServlet
  • 前端控制器请求视图解析器对视图进行解析
  • 视图解析器向前端控制器返回View
  • 前端控制器对视图进行渲染,将数据模型填充到request域
  • 前端控制器向用户响应结果

2.1 SpringMVC九大组件

  • HandlerMapping(处理器映射器):查找Handler,标注@RequestMapping的方法都可以看成一个Handler。负责具体请求处理,请求到达后,找到请求对应的处理器Handler和拦截器Interceptor
  • HandlerAdapter(处理器适配器):执行Handler。
  • HandlerExceptionResolver(异常解析器):用于处理Handler产生的异常情况。
  • ViewResolver(视图解析器):用于将String类型的视图名和Locale解析为View类型的视图。
  • RequestToViewNameTranslator(默认视图名转换器):从请求中获取ViewName。
  • LocaleResolver(区域化解析器):用于从请求中解析出Locale,比如:中国的Locale 是zh-CN。
  • ThemeResolver(主题解析器):用于解析主题。
  • MultipartResolver(多部件解析器):用于上传请求,作用就是封装普通请求,使其拥有文件上传的功能。
  • FlashMapManager:用于重定向时的参数传递。
3. 请求参数绑定
  • 默认支持Servlet API作为方法参数,当需要使⽤HttpServletRequest、HttpServletResponse、HttpSession等原⽣servlet对象时,直接在handler⽅法中形参声明使⽤即可。

  • 绑定简单类型参数

    简单数据类型:八种基本数据类型及其包装类型(推荐),绑定简单数据类型参数,只需直接声明形参即可。

  • 绑定pojo类型参数

    直接声明形参即可,要求参数名必须和pojo的属性名保持一致

  • 绑定pojo包装对象参数

    传参参数名和pojo属性保持一致,如果不能定位数据项,通过属性名.属性的方式进一步锁定数据

  • 绑定日期类型参数(需定义类型转换器)

    • 自定义类型转换器
    public class DateConverter implements Converter<String, Date> {
    	@override
        public Date convert(String source) {
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
            try {
                Date parse = simpleDateFormat.parse(source);
                return parse;
            } catch (ParseException e) {
                e.printStackTrace();
            }
    		return null;
        }
    }
    
    • 注册自定义类型转换器
    <mvc:annotation-driven conversion-service="conversionServiceBean"/>
    <bean id="conversionServiceBean" class="org.springframework.format.support.FormattingConversionServiceF
    actoryBean">
    	<property name="converters">
        	<set>
            	<bean class="com.lagou.edu.converter.DateConverter"></bean>
            </set>
        </property>
    </bean>
    
4. 对Restful风格请求支持

4.1 什么是Restful

Restful 是⼀种 web 软件架构⻛格,它不是标准也不是协议,它倡导的是⼀个资源定位及资源操作的⻛
格。

优点: 结构清晰、符合标准、易于理解、扩展方便。

特性:

  • 资源:网络上的一个实体,或者说是网络上的一个具体信息。
  • 表现层:把资源具体呈现处理的形式,叫做它的表现层。
  • 状态转化:发起一个请求,就代表客户端和服务器的一次交互过程。

示例:

/account/1 HTTP GET 查询id=1的account

/account/1 HTTP DELETE 删除id=1的account

/account/1 HTTP PUT 更新id=1的account

5.Ajax Json交互
  • 前端到后台:前端ajax发生json格式字符串,后台直接接收为pojo参数。(注解@RequestBody)
  • 后台到前端:后台直接返回pojo对象,前端直接接收为json对象或者字符串。(注解@ResponseBody)

5.1 什么是Json

Json是一种与语言无关的数据交互格式,就是一种字符串,用特殊符号{}内表示对象、[]表示数组、""表示属性或值、:表示后者是前者的值。

{“name”: “Michael”}可以理解为是⼀个包含name为Michael的对象

[{“name”: “Michael”},{“name”: “Jerry”}]就表示包含两个对象的数组

5.2 @ResponseBody注解

@ResponseBody注解作用是将controller的方法返回的对象通过适当的转换器转换为指定的格式之后,写入response对象的body区,通常用来返回JSON数据或是XML数据。

5.3 分析SpringMVC使用Json交互

  • 所需jar包
<!--json数据交互所需jar,start-->
<dependency>
	<groupId>com.fasterxml.jackson.core</groupId>
	<artifactId>jackson-core</artifactId>
	<version>2.9.0</version>
</dependency>
<dependency>
	<groupId>com.fasterxml.jackson.core</groupId>
	<artifactId>jackson-databind</artifactId>
	<version>2.9.0</version>
</dependency>
<dependency>
	<groupId>com.fasterxml.jackson.core</groupId>
	<artifactId>jackson-annotations</artifactId>
	<version>2.9.0</version>
</dependency>
  • 示例代码

    • 前端jsp及js代码
    <div>
    	<h2>Ajax json交互</h2>
    	<fieldset>
    		<input type="button" id="ajaxBtn" value="ajax提交"/>
    	</fieldset>
    </div>
    
    $(function () {
        $("#ajaxBtn").bind("click",function () {
            // 发送ajax请求
            $.ajax({
                url: '/demo/handle07',
                type: 'POST',
                data: '{"id":"1","name":"李四"}',
                contentType: 'application/json;charset=utf-8',
                dataType: 'json',
                success: function (data) {
                	alert(data.name);
                }
            })
        })
    })
    
    • 后台代码
    @RequestMapping("/handle07")
    // 添加@ResponseBody之后,不再⾛视图解析器那个流程,⽽是等同于response直接输出
    数据
    public @ResponseBody User handle07(@RequestBody User user) {
    	// 业务逻辑处理,修改name为张三丰
    	user.setName("张三丰");
    	return user;
    }
    

第二部分 SpringMVC高级技术

1. 拦截器(Intecepter)使用

1.1 监听器、过滤器和拦截器对比

  • Servlet:处理Request请求和Response响应

  • 过滤器(Filter):对Request请求进行过滤的作用,作用在Servlet之前,如果配置/*可以对所有的资源(servlet、js/css静态资源等)进行过滤处理

  • 监听器(Listener):实现javax.servlet.ServletContextListener接口的服务器端组件,随web应用启动而生效,只初始化一次,会一直运行监视,web应用停止即销毁。

    作用一:做一些初始化工作,web应用中spring启动ContextLoaderListener

    作用二:监听web应用中特定事件,比如HttpSession,ServletRequest的创建和销毁;变量的创建、修改和销毁等。如:统计在线人数,利用HttpSessionLisener等。

  • 拦截器(Interceptor):是SpringMVC、Struts等表现层框架自己的,不会拦截jsp/html/css/image的访问等,只会拦截访问的控制器方法。

    从配置角度来看:servlet、filter、listener是配置在web.xml中,而interceptor配置在框架自己的配置文件中。

    • 在Handler业务逻辑执行之前拦截一次
    • 在Handler逻辑执行完毕但未跳转页面之前拦截一次
    • 跳转页面之后拦截一次

在这里插入图片描述

1.2 拦截器的执行流程

拦截器的执行是有一定的顺序的,该顺序与配置文件中定义的拦截器的顺序一致,单个拦截器,在程序中的执行流程如下图所示:

在这里插入图片描述

1)程序先执行preHandle()方法,如果该方法的返回值为true,则程序继续向下执行处理器中的方法,否则将不再向下执行。

2)在业务处理器(控制器Controller类)处理完请求后,会执行postHandle()方法,然后通过DispatcherServlet向客户端返回响应。

3)DispatcherServlet处理完请求后,才会执行afterCompletion()方法。

1.3 多个拦截器的执行流程

多个拦截器,在程序中的执行流程如下图所示:

在这里插入图片描述

2. 控制器中处理异常
@ControllerAdvice
public class GlobalExceptionResolver {

	@ExceptionHandler(ArithmeticException.class)
	public ModelAndView handleException(ArithmeticException exception,
	HttpServletResponse response) {
		ModelAndView modelAndView = new ModelAndView();
		modelAndView.addObject("msg",exception.getMessage());
		modelAndView.setViewName("error");
		return modelAndView;
	}
}
3. 基于Flash属性的跨重定向请求数据传递

重定向时请求参数会丢失,往往通过重新携带请求参数,参数拼接如下:

return "redirect:handle01?name=" + name;

但是此方法有局限性,因为携带参数长度有限制,参数安全性也不高。此时,可以使用SpringMVC提供的flash属性机制,向上下文中添加flash属性,框架会在session中记录该属性值,跳转页面之后会自动删除flash属性,通过此方法进行重定向参数传递,参数长度和安全性得到保障。

@RequestMapping("/handleRedirect")
public String handleRedirect(String name,RedirectAttributes
redirectAttributes) {
	//return "redirect:handle01?name=" + name; // 拼接参数安全性、参数⻓度都有局限
	// addFlashAttribute⽅法设置了⼀个flash类型属性,该属性会被暂存到session中,在
	跳转到⻚⾯之后该属性销毁
	redirectAttributes.addFlashAttribute("name",name);
	return "redirect:handle01";
}

第三部分 SpringMVC源码深度剖析

1. 前端控制器 DispatcherServlet继承结构

在这里插入图片描述

2. SpringMVC处理请求流程

时序图如下:

在这里插入图片描述

1)调用getHandler()获取能够处理当前请求的执行联HandlerExecutionChain(Handler+拦截器)

遍历两个HandlerMapping,获取能够处理当前请求的执行链

在这里插入图片描述

2)调用getHandlerAdapter()方法;获取能够执行1)中Handler的适配器

遍历各个HandlerAdapter,找到支持处理当前Handler的适配器

在这里插入图片描述

3)适配器调用Handler执行ha.handle(返回一个ModelAndView对象)

入口:ha.handle()

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4)调用processDispatchResult()方法完成视图渲染跳转

在这里插入图片描述

入口:rander方法

在这里插入图片描述

  • 视图解析器解析出View视图对象

在这里插入图片描述

  • 解析出View视图对象的过程中会判断是否重定向、是否转发等,不同情况封装的是不同的View实现

在这里插入图片描述

  • 解析出View视图对象的过程中,要将逻辑视图名解析为物理视图名

在这里插入图片描述

  • 封装View视图对象之后,调用view对象的render方法

在这里插入图片描述

  • 渲染数据

在这里插入图片描述

  • 把modelMap中的数据暴露到request域中,后台通过el表达式直接取值

在这里插入图片描述

3. SpringMVC九大组件初始化

1)在DispatcherServlet中定义了九个属性,每⼀个属性都对应⼀种组件

// 多部件解析器
private MultipartResolver multipartResolver;

// 国际化解析器
private LocaleResolver localeResolver;

// 主题解析器
private ThemeResolver themeResolver;

// 处理器映射器
private List<HandlerMapping> handlerMappings;

// 处理器适配器
private List<HandlerAdapter> handlerAdapters;

// 异常解析器
private List<HandlerExceptionResolver> handlerExceptionResolvers;

// 默认视图名转换器
private RequestToViewNameTranslator viewNameTranslator;

// flash属性管理器
private FlashMapManager flashMapManager;

// 视图解析器
private List<ViewResolver> viewResolvers;

2)九大组件初始化时机

  • DispatcherServlet中的onRefresh(),此方法初始化了九大组件

在这里插入图片描述

  • initStrategies方法

在这里插入图片描述

  • 观看其中一个组件
private void initHandlerMappings(ApplicationContext context) {
	this.handlerMappings = null;

	if (this.detectAllHandlerMappings) {
		// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
		// 找到所有的HandlerMapping
		Map<String, HandlerMapping> matchingBeans =
				BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
		if (!matchingBeans.isEmpty()) {
			this.handlerMappings = new ArrayList<>(matchingBeans.values());
			// We keep HandlerMappings in sorted order.
			AnnotationAwareOrderComparator.sort(this.handlerMappings);
		}
	}
	else {
		try {
			// 否则在ioc中按照固定名称去找
			HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
			this.handlerMappings = Collections.singletonList(hm);
		}
		catch (NoSuchBeanDefinitionException ex) {
			// Ignore, we'll add a default HandlerMapping later.
		}
	}

	// Ensure we have at least one HandlerMapping, by registering
	// a default HandlerMapping if no other mappings are found.
	if (this.handlerMappings == null) {
		// 最后还为空则按照默认策略生成
		this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
		if (logger.isTraceEnabled()) {
			logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
					"': using default strategies from DispatcherServlet.properties");
		}
	}
}

如果按照类型和固定id去ioc容器中找不到对应组件,则按照默认策略进行初始化,默认策略在DispatcherServlet.properties中配置

  • DispatcherServlet.properties
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
	org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
	org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
	org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
  • 注意:有些组件解析器的初始化必须按照id注册对象(比如multipartResolver、localeResolver、themeResolver等)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值