SpringMVC运⾏时序图解析
《Spring in Action》⼀书中 ,Spring MVC 的核⼼组件和⼤致处理流程如下:
①、DispatcherServlet 是SpringMVC 中的前端控制器(Front Controller),负责接收Request 并将Request 转发给对应的处理组件。
② 、HanlerMapping 是SpringMVC 中完成url 到Controller 映射的组件。DispatcherServlet 接收Request, 然后从HandlerMapping 查处理Request 的Controller。
③、Controller 处理Request,并返回ModelAndView 对象,Controller 是SpringMVC中负责处理Request 的组件(类似于Struts2 中的Action),ModelAndView 是封装结果视图的组件。
④、⑤、⑥视图解析器解析ModelAndView 对象并返回对应的视图给客户端。容器初始化时会建⽴所有url 和Controller 中的Method 的对应关系,保存到HandlerMapping 中,⽤户请求是根据Request 请求的url 快速定位到Controller 中的某个⽅法。在Spring 中先将url 和Controller 的对应关系,保存到Map<url,Controller>中。Web 容器启动时会通知Spring 初始化容器(加载Bean 的定义信息和初始化所有单例Bean),然后SpringMVC 会遍历容器中的Bean,获取每⼀个Controller 中的所有⽅法访问的url,然后将url 和Controller 保存到⼀个Map中;这样就可以根据Request 快速定位到Controller,因为最终处理Request 的是Controller 中的⽅法,Map 中只保留了url 和Controller 中的对应关系,所以要根据Request 的url 进⼀步确认Controller 中的Method,这⼀步⼯作的原理就是拼接Controller 的
url(Controller 上@RequestMapping 的值) 和⽅法的url(Method 上@RequestMapping 的值),与reque
st 的url 进⾏匹配,到匹配的那个⽅法;确定处理请求的Method 后,接下来的任务就是参数绑定,把Request 中参数绑定到⽅法的形式参数上,这⼀步是整个请求处理过程中最复杂的⼀个步骤。
先来看⼀ 张图,DispatcherServlet和HttpServlet的继承关系:
众所周知,SpringMVC的⼊⼝是DispatcherServle,⽽DispatcherServle继承了 FrameworkServlet, FrameworkServlet继承了HttpServletBean, HttpServletBean继承了 HttpServlet, HttpServletBean的init⽅法如下:
在 HttpServletBean的init ⽅法中调⽤了⼦类FrameworkServlet的 initServletBean⽅法:
org.springframework.web.servlet.FrameworkServlet#onRefresh
在 initServletBean⽅法中初始化 WebApplicationContext实例, 并调⽤了onRefresh⽅法
⽽org.springframework.web.servlet.FrameworkServlet的 onRefresh只是⼀个抽象的定义,它的实现其实是在DispatcherServlet 重写的
onRefresh⽅法。在 DispatcherServlet 的 onRefresh0⽅法中⼜调⽤了 initstrategies0⽅法,初始化 SpringMVC的九⼤组件,到这⾥SpringMVC初始化完成。
上⾯复杂的调⽤关系,⼀句话总结就是在 Servlet的 init() ⽅法中初始化了IOC容器和SpringMVC的九
⼤组件,流程概括如下:
那么,URL和Controller之间的关系是如何建⽴的呢? HandlerMapping是个接⼝。先来看⼀下HandlerMapping 的实现类,会看到⼀个AbstractDetectingUrlHandlerMapping,可以看到其实现了ApplicationContextAware,在Spring容器会检测容器中的所有Bean,如果发现某个Bean实现了ApplicationContextAware接⼝,Spring容器会在创建该Bean之后,⾃动调⽤该Bean的setApplicationContextAware()⽅法。看看他的类图再慢慢去寻这个触发点。
会在ApplicationObjectSupport 发现了这个⽅法,继⽽调⽤到了HandlerMapping 的⼦类AbstractDetectingUrlHandlerMapping 中的initApplicationContext()⽅法,所以我们直接看⼦类中的初始化容器⽅法:
//建⽴当前ApplicationContext 中的所有Controller 和url 的对应关系
protected void detectHandlers()throws BeansException {
ApplicationContext applicationContext =obtainApplicationContext();
if(logger.isDebugEnabled()){
logger.debug("Looking for URL mappings in application context: "+ applicationContext);
}
// 获取ApplicationContext 容器中所有bean 的Name
String[] beanNames =(this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class):
// Take any bean name that we can determine URLs for.
// 遍历beanNames,并到这些bean 对应的url
for(String beanName : beanNames){
// bean 上的所有url(Controller 上的url+⽅法上的url),该⽅法由对应的⼦类实现
String[] urls =determineUrlsForHandler(beanName);
if(!ObjectUtils.isEmpty(urls)){
// URL paths found: Let's consider it a handler.
// 保存urls 和beanName 的对应关系,put it to Map<urls,beanName>,
// 该⽅法在⽗类AbstractUrlHandlerMapping 中实现
registerHandler(urls, beanName);
}
else{
if(logger.isDebugEnabled()){
logger.debug("Rejected bean name '"+ beanName +"': no URL paths identified");
}
}
}
}
/** 获取Controller 中所有⽅法的url,由⼦类实现,典型的模板模式**/
protected abstract String[]determineUrlsForHandler(String beanName);
determineUrlsForHandler(String beanName)⽅法的作⽤是获取每个Controller 中的url,不同的⼦类有
不同的实现,这是⼀个典型的模板设计模式。因为开发中我们⽤的最多的就是⽤注解来配置Controller 中的url , BeanNameUrlHandlerMapping 是AbstractDetectingUrlHandlerMapping 的⼦类,处理注解形式的url 映射.所以我们这⾥以BeanNameUrlHandlerMapping 来进⾏分析。我们看BeanNameUrlHandlerMapping 是如何查beanName 上所有映射的url。
protected String[]determineUrlsForHandler(String beanName){
List<String> urls =new ArrayList<>();
if(beanName.startsWith("/")){
urls.add(beanName);
}
String[] aliases =obtainApplicationContext().getAliases(beanName);
for(String alias : aliases){
if(alias.startsWith("/")){
urls.add(alias);
}
spring ioc注解}
StringArray(urls);
}
到这⾥HandlerMapping 组件就已经建⽴所有url 和Controller 的对应关系。
请求处理阶段:
 这⼀步步是由请求触发的,所以⼊⼝为DispatcherServlet 的核⼼⽅法为doService(),doService()中的核⼼逻辑由doDispatch()实现,源代码如下:
/** 中央控制器,控制请求的转发**/
protected void doDispatch(HttpServletRequest request, HttpServletResponse response)throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed =false;
WebAsyncManager asyncManager = AsyncManager(request);

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。