springmvc源码学习

软件发布|下载排行|最新软件

当前位置:首页IT学院IT技术

springmvc源码学习

小小少年-   2019-12-15 我要评论

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有三种配置方式
1.@Controller注解
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几次,就可以看得明白了

Copyright 2022 版权所有 软件发布 访问手机版

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 联系我们