前言
在Spring源码的阅读中,我们看到了Spring预留了很多的拓展点,这写拓展点可以供我们自定义实现,也会支持与其它框架的整合,比如之前讲解的mybatis。今天,带大家深入的了解以下关于Spring主子容器的运用SpringMvc的源码知识,希望可以有所收获;
一、原生的Severlet
在开始SpringMvc的源码讲解之前,想要和大家一起回顾一下原生Severlet,SpringMVC就是在此基础上加以优化和封装而来的;
- 代码回顾:
package com.ys.servlet;
import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class HelloServlet implements Servlet{
//只被调用一次,第一次请求Servlet时,创建Servlet的实例,调用构造器
public HelloServlet() {
System.out.println("构造器 HelloServelt()...");
}
//该方法用于初始化Servlet,就是把该Servlet装载入内存
//只被调用一次,在创建好实例后立即被调用
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("初始化方法 init()...");
}
//被多次调用,每次请求都会调用service方法。实际用于响应请求的
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
System.out.println("执行方法主体 service()...");
}
//只被调用一次,在当前Servlet所在的WEB应用被卸载前调用,用于释放当前Servlet所占用的资源
@Override
public void destroy() {
System.out.println("servlet 销毁时调用方法 destroy()...");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public String getServletInfo() {
return null;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<!--在tomcat 服务器中运行时,如果不指名访问文件名,默认的根据项目名访问文件顺序如下配置 -->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<!--给创建的 Servlet 配置映射关系 -->
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.ys.servlet.HelloServlet</servlet-class>
<!--servlet的完整名称-->
</servlet>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<!-- 与上面配置的 servlet-name 名字要对应,一个servlet可以有多个 servlet-mapping -->
<url-pattern>/hello</url-pattern>
<!--访问路径-->
</servlet-mapping>
</web-app>
- 代码中可以看到,通过Servlet实现动态web应用的基本简单基本步骤,通过与tomcat的搭配,会调用web.xml中配置的指定servlet的处理器中,调用顺序大致时:init-》service-》destroy,且一个servlet项目只会调用一次init和destroy方法;
二、SpringMvc是什么?
- MVC设计思想:M数据层,可以是dao层和Service层,V视图层,可以理解为JSP页面,C控制层,可以理解为Sererlet处理层(DispatcherServlet)控制转发调用;
- Spring MVC和servlet的区别是什么:1,思想层面,servlet只能是mvc设计架构的一部分,collector部分。2,springMvc隶属于Spring全家桶的一部分,所有的spring的优点都可以用到mvc上;3,约定大于配置,不要一个一个去配置servlet,简化开发流程;4,异常处理:Spring MVC提供了统一的异常处理机制,使异常处理更加清晰、一致。而Servlet需要手动处理异常。5,配置管理:Spring MVC的配置更加简洁、灵活,可以通过XML配置文件、注解等方式进行配置。而Servlet需要手动配置很多细节,如请求映射、初始化参数等。
三、SpringMvc组件?
- 前端控制器 DispatcherServlet
- 处理器映射器 HandlerMapping
- 处理器适配器 HandlerAdapter
- 视图解析器(ViewResolver)
- 视图渲染(View)
三、SpringMvc源码执行步骤?
1.容器加载
-
servlet上下文监听器的contextInitialzed方法完成对Spring配置文件的解析工作(完成对父容器的解析);
-
调用到HttpServlet的init方法完成对SPringMvc容器的解析工作,在容器中指定了监听器,在完成refresh监听后会执行九大内置主键的解析:
-
MultipartResolver:MultipartResolver用于处理上传请求,处理方式是将普通的request包装成MultipartHttpServletRequest,可以直接调用getFile方法来获取File,如果上传多个文件,可以调用getFileMap来处理。
-
LocaleResolver:在ViewResolver解析视图的时候,使用到国际化资源或者主题的时候,例如根据请求头里的locale对象来确定用户的地域设置,自己写逻辑获取指定地区配置的商品价格,信息等数据。
-
ThemeResolver:主题设置,可以指定多套主题css图片/字体/格式,进行配置读取指定。
-
HandlerMappings:处理器映射器,将请求映射到对应的handler处理器中;初始化HandlerMappings对象时,先会在beanFactory中找,找不到会使用DispatcherServlet_test.properties(见下图)中默认定义的BeanNameUrlHandlerMapping,RequestMappingHandlerMapping,RouterFunctionMapping三个映射器;
在初始化BeanNameUrlHandlerMapping对象时,由于该对象ApplicationContextAware,在spring完成实例化后再执行init方法之前会调用到其setApplicationContext方法,在调用该方法时完成了对以 / 开头bean对象put到其handlerMap属性集合中;
在实例化RequestMappingHandlerMapping时,由于该类实现了InitializingBean方法,在执行完属性赋值后会调用其afterPropertiesSet,在该方法完成对注解的扫描与解析工作; -
HandlerAdapters:处理器适配器,真正调用处理器且完成了对响应结果的处理,适配器模式的使用,用于将请求适配对应的处理器;初始化流程与HandlerMappings大致相同,默认定义HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter,RequestMappingHandlerAdapter,HandlerFunctionAdapter四种适配器;
-
HandlerExceptionResolver:基于HandlerExceptionResolver接口的异常处理,初始化流程与HandlerMappings大致相同,默认定义ExceptionHandlerExceptionResolver,ResponseStatusExceptionResolver,DefaultHandlerExceptionResolver三种集中异常处理器;
-
RequestToViewNameTranslator:当controller处理器方法没有返回一个View对象或逻辑视图名称,并且在该方法中没有直接往response的输出流里面写数据的时候,spring将会采用约定好的方式提供一个逻辑视图名称,比如“找不到页面”;
-
ViewResolvers:将ModelAndView选择合适的视图进行渲染的处理器;
-
FlashMapManager:用于存储并检索FlashMap。FlashMap主要用于在重定向中传递参数。
2.请求调用(分发及处理)
-
具体的调用过程
-
适配器具体执行过程
非常典型的适配器模式的使用,为了后续便于维护调用不同类型handler处理器拓展工作。在现有的三种处理器中,实现coltroller接口和使用HttpRequestHandler接口的处理器(controller)比较方便调用,直接调用对应处理器的handleRequest方法即可,但是使用@Controller注解实现的RequestMappingHandlerAdapter处理器不确定需要调用哪个方法进行执行,所有处理逻辑比较复杂,下面对处理注解方式实现hanlder的适配器进行详细的流程介绍;
1,初始化解析 @initBounder注解/@ModelAttribute注解/@SessionAttribute注解
2,解析到方法的参数
3, 使用argumentResolvers解析参数
4,在参数接卸过程中会匹配到对应的initBounder进行对参数的增强
5,调用到对应处理器的方法
6,处理@ResponseStatus注解
7,使用returnValueHandlers处理返回值
8,完成对model的重新赋值
9,自定义后置处理器调用
10,完成view页面渲染 -
initBounder注解的作用:
在controller控制器里的一个针对于当前控制器的初始化的自定义数据绑定规则,可以理解为调用到具体的方法前的拦截器,可以在调用具体方法之前进行数据校验及数据类型转换的工作; -
@ModelAttribute注解:
可以理解为一次请求中对model对象的赋值与运用,可以作用在参数中或方法上;在方法上可以对一次请求中的model进行赋值,在后续调用过程中可以直接获取到model并使用,作用在参数中可以将model中指定属性赋值到被修饰的参数上,方便后续使用; -
@SessionAttribute注解:
与ModelAttribute有相适之处,只是SessionAttribute作用在session不仅限于一次请求,且SessionAttribute只能运用在类的层面,作用就是将model指定的值放在Session存储,范围比ModelAttribute大;