springmvc源码流程图
2022-05-24 17:27:01 0 举报
springmvc父子容器启动,springmvc请求分发源码流程
作者其他创作
大纲/内容
doDispatch()这是DispatcherServlet最核心的方法
configureAndRefreshWebApplicationContext()
核心方法: - InternalResourceViewResolver.buildView(viewName) - 创建的View是JstlView extend InternalResourceView InternalResourceViewResolver的无参构造方法设置了viewClass是JstlView.class - view.setPreventDispatchLoop(true) 设置为true后请求死循环会抛错
FrameworkServlet.initServletBean
javaConfig方式启动
执行HandlerExecutionChain对象中拦截器的的postHandler后置方法
遍历所有WebApplicationInitializer类排除接口和抽象类
根据返回值的类型找到合适的返回值解析器来解析返回值
这个方法里就是spring的启动过程
applicationContext.getBeanNamesForType(Object.class)
执行render去渲染
没有
是
将request和response封装到ServletWebRequest对象中
bestMatch.getHandlerMethod()返回最匹配的HandlerMethod
方法是否有@RequestMapping注解
initWebApplicationContext()
其他情况(转发到jsp)/WEB-INF/jsp/a.jsp
getDirectPaths(RequestMappingInfo)
视图名称以 redirect: 开头
返回值是ModelAndView的话- ModelAndViewMethodReturnVlaueHandler- 构建一个新的ModelAndView对象,属性值从mavContainer中拿当前执行的方法有@ResponseBody注解或Controller有@ResponseBody注解- RequestResponseBodyMethodProcessor- mavContainer.setRequestHandled(true)- 返回null
ModelAndView为空(返回值不是ModelAndView)
新建一个mergeModel合并modelMapmergedModel这个Map中的数据set到request中
执行处理器Handler(Controller,也叫页面控制器,执行controller中的方法),返回ModelAndView
processCandidateBean(beanName)
该Controller有@Controller或@RequestMapping注解
全局配置(ControllerAdvice)的有@InitBinder的方法已经在初始化HandlerAdapter的时候就加载到缓存中了
createRequestMappingInfo(method)
渲染子流程
排序后拿第一个Match
创建RequestMappingInfo对象时的合并前缀跨域考虑initHandlerMapping和initHandlerAdapter要不要也画下流程图异步@InitBinder@ModelAttribute
wac.refresh()
ha.handler子流程
不同
执行HandlerExecutionChain中HandlerInterceptor拦截器的afterCompletion方法
解析视图名称,根据不同的viewName来创建不同类型的View并缓存,下次就可以直接拿缓存中的
applicationContext().getType(beanName)拿到该beanName的类型
beanName -> beanType
返回值是ModelAndView的话- ModelAndViewMethodReturnVlaueHandler当前执行的方法有@ResponseBody注解或Controller有@ResponseBody注解- RequestResponseBodyMethodProcessor- 如果有实现ResponseBodyAdvice的ControllerAdvice就会在输出前执行advice的方法
我们在配置DispatcherServlet的时候,将其设置为启动时创建实例,所以Tomcat在启动的时候就会创建Spring的子容器。
子流程
1. 用父容器来管理非Controller的bean(也就是service、dao层的bean),子容器来管理Controller的bean,这样方便于划分框架的边界。2. 规范整体架构,子容器可以访问父容器而父容器不能访问子容器(体现在Controller可以访问service层,但是service层不能访问Controller)3. 方便子容器的切换,web层如果要改框架的话就只需要更改web层的东西就行了,父容器不用变。
拿到合适的参数解析器解析参数反射执行method得到返回值
容器启动阶段初始化:拿到所有实现了HandlerMapping的Bean放入handlerMappings
解析请求映射子流程
如果returnVlaue返回的ModelAndView对象中设置的是View对象,那么就不需要去解析了 ---> 直接去执行View.render()方法
根据映射路径来排序的时候 根据精准度排序 大概是这样的: ? > * > {} >** 具体可以去看: AntPatternComparator
detectHandlerMethods(beanName)
registerDispatcherServlet去创建子容器
拿第二个匹配Math和第一个比较如果相同则抛异常
RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
DispathcerServlet初始化(子容器启动)
根据路径在pathLookup中找 -> 没找到则去遍历所有RequestMappingInfo-> 各Condition属性匹配request的method、header等-> 最后将匹配的mapping封装到Match对象添加到matches
FrameServlet.service()-> HttpServlet.service()
pathLookup、registry会在RequetsMappingHandlerMapping创建bean初始化的时候加载数据。
看Controller上是否有@RequestMapping注解
拿到转发路径
抛异常
找到全局配置的有@ModelAttribute和当前Controller中有@ModelAttribute注解的Method封装到InvocableHandlerMethod对象 -> 添加到List<InvocableHandlerMethod> -> 封装到ModelFactory
拿到重定向的地址
创建HandlerExecutionChain对象封装handler(这是HandlerMethod对象)和拦截器
contextConfigLocation - classpath:spring-mvc
相同
方法上有@RequestMapping注解则将注解的属性封装到RequestMappingInfo对象中 -> 如果handler(controller)类上也有@RequestMapping注解,则会将两个RequestMappingInfo对象的属性合并
handlerMethodsInitialized(getHandlerMethods())
Look for handler methods in the specified handler bean将Controller中符合的mapping info缓存起来
ServletContext是tomcat传进来的
不需要去解析的路径,找精确的路径 /user/add,没有 ?、*、{、}这些
如果返回值不是ModelAndView,那么这里返回的是个null。也就不需要再去渲染视图了。返回值在返回值处理器那边就会响应出去
ContextLoaderListener.contextInitialized()
xml方式的话,这边的流程是不需要的
initStrategies
让创建的View经历一下bean生命周期中的初始化前、初始化、初始化后
config属性赋值
isHandler(beanType)
根据HttpMethod来判断执行doDet、doPost还是别的
以forward: 开头(转发到action)forward:/user/addUser
会将参数解析器、返回值解析器、InitBinder解析器设置进该对象中后面执行和返回的时候会用到
返回null
createWebApplicationContext()
request.getRequestDispatcher(\"/user/add.jsp\
javaconfig方式已经在前面创建好了ApplicationContext,上面一步就不需要执行了,直接可以启动容器了
从request中解析出请求映射路径/request/mapping
SPI机制,tomcat会去加载spring-web包下的/WEB-INF/services目录下的javax.servlet.ServletContainerInitializer文件,进入进入文件中设置的类SpringServletContainerInitializer,执行onStartUp方法,最后去执行自己定义的WebApplicationInitializer的onStartUp方法。
将HandlerMethod添加到nameLookup
方法上没有的话这里不会去找Controller类上有没有@RequestMapping注解直接返回null。Controller类上的@RequestMapping注解是用来合并用的,如果没有@RequestMapping的方法,那么是没法路由进来的(no mapping for GET/test)
构建一个新的ModelAndView对象,将mavContainer中值给它
InternalResourceViewResolver中给视图名称拼接上前缀后缀 -> 构建InternalResourceView(应该是子类JstlView)
需要验证一下请求进来的映射路径和转发路径是否相同,相同则抛异常防止死循环的发生
遍历handlerMappings执行getHandler,方法主要匹配了就返回下面的HandlerMapping不会再去走了这里主要看RequestMappingHandlerMapping
Controller上有注解的话就会合并RequestMappingInfo对象最后返回合并之后的对象
全局配置(ControllerAdvice)的有@ModelAttribute注解的方法已经在初始化HandlerAdapter的时候就加载到缓存中了
创建父容器- XML方式需要去创建ContextLoader.properties这个配置文件有默认的父容器的类型(XmlWebApplicationContext)- javaconfig方式不需要去创建前面已经建好了
afterPropertiesSet
processDispatchResult渲染视图,响应用户
createWebApplicationContext()创建ApplicationContext
getHandler解析请求映射-> 返回HandlerExecutionChain
UserController.addUser ->UC#addUser
ModelAndView
Xml方式的化会拿contextConfigLocation参数值
该方法上是否有@RequestMapping注解 -> 有的话就会将@RequestMapping的属性封装成一个个的RequestCondition对象 -> 最后封装进RequestMappingInfo对象中所以,一个Method对应一个RequestMappingInfo
如果方法参数有@RequestBody注解,那么ConsumesRequestCondition设置属性(setBodyRequired)
构建ModelAndViewContainer
遍历handlerAdapters,找到第一个匹配的handlerAdapter
构建RedirectView对象
super.onStartUp()去创建父容器
@HandlesTypes(WebApplicationInitializer.class)spring会去找这个注解设置的WebApplicationInitializer.class的类
执行HandlerExecutionChain对象中拦截器的的preHandler前置方法
response.sendRedirect(encodedURL)进行重定向
refresh执行spring启动流程
HandlerMethod
最后都会进到FrameworkServlet的processRequest方法中
Tomcat启动的时候会依次加载web.xml中配置的Listener、Filter和Servlet。所以根据上面的配置,会首先加载ContextLoaderListener,这个类继承了ContextLoader,用来初始化Spring父容器,并将其放入ServletContext中。如果web.xml中没有配置ContextLoaderListener那么父容器就不会创建。
DispatcherServlet转化为BeanWapper添加pvs
执行转发方法
创建子容器、创建DispatcherServlet添加到ServletContext
HandlerExecutionChain
子流程(这里就演示一下RequestMappingHandlerMapping的初始化流程)
遍历当前容器所有的beanName
程序员一般定义一个InternalResourceViewResolver
finishRefresh -> publishEvent (发布事件,执行监听器)->SourceFilteringListener ->GenericApplicationListenerAdapter -> ContextRefreshListener -> onRefresh ->DispatcherServlet.onRefresh()
最后会将父容器添加到SerlvetContext这个域中方便子容器调用
AbstractDispatcherServletInitializer.onStartUp()
tomcat通过spi机制执行SpringServletContainerInitializer的onStartUp方法
DispatcherServlet.doService()
启动完成,用户请求这里我用的是RequesMappingHandlerMapping
父容器启动ContextLoaderListener
否
构建ServletInvocableHandlerMethod
创建父容器,将ContextLoaderListener添加到ServletContext中new ContextLoaderListener的时候会去super父类ContextLoader将ApplicationContext加载进去
异步处理
configureAndRefreshWebApplicationContext进入
这个方法会初始化一些解析器、HandlerMapping、HandlerAdapter等,我主要这里讲下initHandlerMappings和initHandlerAdapters。1. initHandlerMappings- 如果detectAllHandlerMappings=true,那么根据HandlerMapping类型找bean,否则直接根据指定的beanName找:getBean(\"handlerMapping\
所以javaconfig方式来启动的时候需要自己写个WebApplicationInitializer类作为启动器(继承这个类就行了AbstractAnnotationConfigDispatcherServletInitializer)
new RequestMappingInfo.BuilderConfiguration()
matches如果有多个匹配的
super.afterPropertiesSet() ->initHandlerMethods()
getHandlerInternal返回HandlerMethod对象
该方法不做什么处理就是记录下当前Controller中有多少个RequestMapping
构建InternalResourceView对象
xml方式:spring-mvc.xml如果使用了这个<mvc:annotation-driven/>,那么在spring初始化refresh的时候MvcNameSpaceHandler就会注册一个AnnotationDrivenBeanDefinitionParser解析器,解析的时候会调用registerDefaultComponents方法注册一些默认的HandlerMapping、HandlerAdapte等等。
HttpServletBean.init()
执行剩下的符合条件的WebApplicationInitializer的onStartUp方法
找到全局配置的有@InitBinder和当前Controller中有@InitBinder注解的Method封装到InvocableHandlerMethod对象 -> 添加到List<InvocableHandlerMethod> -> 封装到ServletRequestDataBinderFactory对象
xml方式DispatcherServlet初始化的时候容器是空的需要去创建。创建子容器的时候会去设置父容器(如果web.xml中配置了ContextLoaderListener,那么就会先创建父容器,如果没配置那么父容器就是空的,所有bean都由子容器管理)。
收藏
0 条评论
下一页