springmvc源码学习

1.springmvc运行流程

流程图是直接在百度图片中找的一张

>.前台发送请求,请求会首先通过DispatcherServlet进行url的匹配;如果匹配不到,看是否配置<mvc:default-servlet-hanler>  如果配置了,就找对应的目标资源

>.如果匹配到url,就调用HandlerMapping,获取到handlerExecutionChain

>.dispatcherServlet会调用handlerAdapter

>.handlerAdapter调用对应的handler,也就是controller方法。

>.调用完成之后,返回modelAndView

>.视图解析器会解析出对应的view,并进行视图的渲染

2.springMVC的核心类和接口

前端控制器  DispatcherServlet

处理器映射器 HandlerMapping

处理器适配器 HandlerAdapter

视图解析器   viewResolver

ModelAndView

3.springmvc的controller有三种配置方式

[email protected]注解

2.实现Controller接口,这种方式,需要在类名增加@Component("/映射地址")

3.实现HttpRequestHandler接口,在类上加@Component("/映射地址")

后面两种原理是一样的,下面会说到


springmvc的源码可以分为两部分来说,分别是启动(初始化)和调用;

一、springmvc启动源码:

springmvc启动流程:

  1.springboot在启动的时候,会调用refresh()方法,在refresh()方法中,有一个onRefresh()方法,在spring源码中是一个空方法,springboot中的ServletWebServerApplicationContext实现了onRefresh()方法,所以会调用到这个类的onRefresh方法

  2.在onRefresh()方法中,会先创建dispatcherServlet;然后再创建Tomcat

      2.1 把dispatcherServlet单独拆开来说,在DispatcherServlet类中,有一个静态代码块,会把dispatcherServlet.properties中的属性加载到Properties中,在后面调用的流程中会用到

  3.在实例化所有的beanDefinition的时候,有两个beanDefinition和springmvc源码有关系:BeanNameHandlerMapping和RequestMappingHandlerMapping

RequestMappingHandlerMapping是在org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.EnableWebMvcConfiguration这里,在springboot自动注入的时候,给注入到beanDefinitionMap中了

BeanNameHandlerMapping 是在org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport中注入的

  4.spring在实例化bean的时候,会从beanDefinitionMap中依次获取beanDefinition,当初始化RequestMappingHandlerMapping的时候,由于该类的父类实现了InitializingBean接口,所以会调用对应的afterPropertiesSet()方法;在调用after...方法的时候,最终会调用到这里:

在调用这个方法之前,也是从beanDefinitionMap中拿出所有的beanName,然后再根据beanName获取到Class,判断当前class是否添加了Controller注解或者@RequestMapping注解

  

 1 protected void detectHandlerMethods(Object handler) {
 2     Class<?> handlerType = (handler instanceof String ?
 3             obtainApplicationContext().getType((String) handler) : handler.getClass());
 4
 5     if (handlerType != null) {
 6         Class<?> userType = ClassUtils.getUserClass(handlerType);
 7         //获取到所有添加了@RequestMapping()注解的方法
 8         Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
 9                 (MethodIntrospector.MetadataLookup<T>) method -> {
10                     try {
11                         return getMappingForMethod(method, userType);
12                     }
13                     catch (Throwable ex) {
14                         throw new IllegalStateException("Invalid mapping on handler class [" +
15                                 userType.getName() + "]: " + method, ex);
16                     }
17                 });
18         if (logger.isTraceEnabled()) {
19             logger.trace(formatMappings(userType, methods));
20         }
21         //遍历获取到的方法,然后把映射关系存储起来;mapping是映射地址,invocableMethod是处理的方法,handler是类(controller)
22         methods.forEach((method, mapping) -> {
23             Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
24             registerHandlerMethod(handler, invocableMethod, mapping);
25         });
26     }
27 }

这个方法,是遍历当前controller中,所以添加了@RequestMapping注解的方法

      4.1. 对所有的方法,进行遍历,mapping是requestMapping注解的映射地址,handler就是当前controller类

      4.2. 最终会调用org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#register方法,把当前映射地址存到urlLookUp中,再把url映射地址和对应的处理方法存到了mappingLookup这个map中

也就是说:urlLookUp这个map中key和value都是requestMapping注解对应的映射路径;mappingLookup中的key是映射路径,value是注解对应的处理方法

  5.在spring容器实例化BeanNameUrlHandlerMapping的时候,在调用到bean的初始化方法 PostProcessorBeforeInitialization(org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization)的时候,由于这个类的父类实现了ApplicationContextAware接口,所以,会调用setApplicationContext()方法

      5.1 在org.springframework.context.support.ApplicationObjectSupport#setApplicationContext方法中,会调用到子类的initApplicationContext方法,

      5.2 在initApplicationContext方法中,会获取到单实例池中所有的bean,由于实现controller接口的类上是通过@Component来指定映射路径的,所以,只需要过滤,beanName是以 / 开头的bean即可

      5.3 然后将beanName(/XXX)和对应的handler存到handlerMap中即可

二、springmvc调用流程:

我们直接来说DispatcherServlet的doDispatch()方法,前面的调用链是这样的

  1.当项目启动之后,前端发起请求时,会尝试获取当前请求需要用哪个handlerMapping来处理

      1.1  会遍历所有的handlerMapping ,根据request中的映射地址,来判断当前handlerMapping是否可以处理,

    RequestMappingHandlerMapping会根据映射地址从urlLokUp中get数据,get到之后,再从mappingLookup中get到处理当前请求的方法,返回,然后包装成拦截器链HandlerExecutionChain,拦截器链是一些interceptor

    BeanNameUrlHandlerMapping是根据映射地址从handlerMap中get数据,

    哪个handlerMapping能根据URL获取到数据,就用哪个handlerMapping来处理

  2.获取到拦截器链之后,再获取到拦截器适配器(handlerAdapter),如果是@Controller注解的controller,那获取到的handlerMapping是HandlerMethod类型的;如果是Controller接口,那么是HttpRequestHandler类型的

    2.1 在判断使用哪个handlerAdapter来调用handler的时候,是根据当前handlerMapping的类型来判断的;org.springframework.web.servlet.HandlerAdapter#supports

  3.获取到handlerAdapter之后,会调用目标方法,然后返回;


总结:

sprigmvc的源码,大致的流程是这样的:

在初始化spring容器的时候,会把controller的映射地址和对应的处理方法存入到map中

在发起请求的时候,会根据URL从不同的map中查找处理方法(@Controller注解这种方式的controller,map中存的是具体的处理方法;实现Controller接口这种方式,map中直接存储的是handler类)

根据handlerMapping再获取到handlerAdapter

调用目标方法之前,先执行拦截器的方法,然后再执行目标方法

最后返回到前端;

关于springmvc的源码,也是debug了一次又一次,多debug几次,就可以看得明白了

原文地址:https://www.cnblogs.com/mpyn/p/12044674.html

时间: 2024-08-01 12:59:06

springmvc源码学习的相关文章

SpringMVC源码学习之request处理流程

目的:为看源码提供调用地图,最长调用逻辑深度为8层,反正我是springMVC源码学习地址看了两周才理出来的. 1.处理流程(版本为4.3.18) 入口为spring-webmvc-4.3.18.RELEASE.jar中org.springframework.web.servlet.DispatcherServlet.doService(request,respose)处理session中的flashmap springMVC源码学习之addFlashAttribute源码分析 -->doDis

springMvc源码学习之:spring源码总结

转载自:http://www.cnblogs.com/davidwang456/p/4213652.html spring beans下面有如下源文件包: org.springframework.beans, 包含了操作java bean的接口和类.org.springframework.beans.annotation, 支持包,提供对java 5注解处理bean样式的支持.org.springframework.beans.factory, 实现spring轻量级IoC容器的核心包.org.

springMvc源码学习之:spirngMvc获取请求参数的方法

一.      通过@PathVariabl获取路径中的参数 @RequestMapping(value="user/{id}/{name}",method=RequestMethod.GET) public String printMessage1(@PathVariable String id,@PathVariable String name, ModelMap model) { System.out.println(id); System.out.println(name);

springMvc源码学习之:利用springMVC随时随地获取HttpServletRequest等对象

一.准备工作: 在web.xml中添加 [html] view plain copy <listener> <listener-class> org.springframework.web.context.request.RequestContextListener </listener-class> </listener> 二.使用方法: 1.方法一:通过代码实现 [java] view plain copy HttpServletRequest requ

Spring源码学习笔记(1)

SpringMVC源码学习笔记(一) 前言----   最近花了些时间看了<Spring源码深度解析>这本书,算是入门了Spring的源码吧.打算写下系列文章,回忆一下书的内容,总结代码的运行流程.推荐那些和我一样没接触过SSH框架源码又想学习的,阅读郝佳编著的<Spring源码深度解析>这本书,会是个很好的入门.  进入正文,首先贴上SpringMVC的图片(来自https://docs.spring.io/spring/docs/current/spring-framework

【转】springmvc源码分析链接

SpringMVC源码 SpringMVC源码分析系列 说到java的mvc框架,struts2和springmvc想必大家都知道,struts2的设计基本上完全脱离了Servlet容器,而 springmvc是依托着Servlet容器元素来设计的,同时springmvc基于Spring框架,Spring框架想必搞java的同学都很熟 悉. 一进Spring的官网就发现了这样一排醒目的文字, spring可以让我们构造简单的.便携的.又快又易于扩展的基于jvm的系统和应用程序. 没错,基于Spr

SpringMVC源码分析系列

说到java的mvc框架,struts2和springmvc想必大家都知道,struts2的设计基本上完全脱离了Servlet容器,而springmvc是依托着Servlet容器元素来设计的,同时springmvc基于Spring框架,Spring框架想必搞java的同学都很熟悉. 一进Spring的官网就发现了这样一排醒目的文字, spring可以让我们构造简单的.便携的.又快又易于扩展的基于jvm的系统和应用程序. 没错,基于Spring的MVC框架SpringMVC同样也可以构造具有这些特

Spring源码学习笔记(3)

Spring源码学习笔记(三) 前言----     最近花了些时间看了<Spring源码深度解析>这本书,算是入门了Spring的源码吧.打算写下系列文章,回忆一下书的内容,总结代码的运行流程.推荐那些和我一样没接触过SSH框架源码又想学习的,阅读郝佳编著的<Spring源码深度解析>这本书,会是个很好的入门. DispatcherServlet 实现核心功能 和普通的 Servelt 类一样, DispatcherServlet 中的 doGet() 和 doPost() 方法

SpringBoot源码学习系列之异常处理自动配置

1.源码学习 先给个SpringBoot中的异常例子,假如访问一个错误链接,让其返回404页面 在浏览器访问: 而在其它的客户端软件,比如postman软件: 很显然,在浏览器里访问才会返回页面,而在Postman直接返回json数据了,所以基于此现象,可以跟一下Springboot异常自动配置的原理,本博客基于学习了尚硅谷课程之后,自己动手实践再做的笔录 SpringBoot的异常自动配置类是ErrorMvcAutoConfiguration.java,可以简单跟一下源码: package o