springmvc源码学习
小小少年- 人气:01.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几次,就可以看得明白了
加载全部内容