[转帖]SpringMVC源码解析(上) _Android, Python及开发编程讨论区_Weblogic技术|Tuxedo技术|中间件技术|Oracle论坛|JAVA论坛|Linux/Unix技术|hadoop论坛_联动北方技术论坛  
网站首页 | 关于我们 | 服务中心 | 经验交流 | 公司荣誉 | 成功案例 | 合作伙伴 | 联系我们 |
联动北方-国内领先的云技术服务提供商
»  游客             当前位置:  论坛首页 »  自由讨论区 »  Android, Python及开发编程讨论区 »
总帖数
5
每页帖数
101/1页1
返回列表
0
发起投票  发起投票 发新帖子
查看: 2861 | 回复: 4   主题: [转帖]SpringMVC源码解析(上)         下一篇 
    本主题由 koei123 于 2015-2-5 17:25:19 移动
yanghui
注册用户
等级:上尉
经验:584
发帖:92
精华:0
注册:2011-12-13
状态:离线
发送短消息息给yanghui 加好友    发送短消息息给yanghui 发消息
发表于: IP:您无权察看 2015-1-12 10:33:49 | [全部帖] [楼主帖] 楼主

1.从DispatcherServlet开始
     与很多使用广泛的MVC框架一样,SpringMVC使用的是FrontController模式,所有的设计都围绕DispatcherServlet为中心来展开的。见下图,所有请求从DispatcherServlet进入,DispatcherServlet根据配置好的映射策略确定处理的Controller,Controller处理完成返回ModelAndView,DispatcherServlet根据配置好的视图策略确定处理的View,由View生成具体的视图返回给请求客户端。
2.初始化
    SpringMVC几个核心的配置策略包括:
    *HandlerMapping:请求-处理器映射策略处理,根据请求生成具体的处理链对象
    *HandlerAdapter:处理器适配器,由于最终的Handler对象不一定是一个标准接口的实现对象,参数也可能非常的灵活复杂,因此所有的对象需要一个合适的适配器适配成标准的处理接口来最终执行请求
    *ViewResolver:视图映射策略,根据视图名称和请求情况,最终映射到具体的处理View,由View对象来生成具体的视图。
    其他的配置策略包括MultipartResolver、LocaleResolver、ThemeResolver等等,但并不影响我们对整个SpringMVC的工作原理的理解,此处并不具体说明。
1)初始化Context
     见下图DispatcherServlet的继承结构,其中,HttpServletBean主要功能是在初始化(init)时将servlet的配置参数(init-param)转换成Servlet的属性,FrameworkServlet主要功能是与ApplicationContext的集成,因此Context的初始化工作主要在FrameworkServlet中进行。
     Context初始化的过程可以通过如下过程来描述:HttServletBean.init –> FrameworkServlet.initServletBean –> FrameworkServlet.initWebApplicationContext。具体的初始化过程可见如下代码:

FrameworkServlet.initWebApplicationContext


Java代码 复制代码

protected WebApplicationContext initWebApplicationContext() throws BeansException {
      WebApplicationContext parent = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
      WebApplicationContext wac = createWebApplicationContext(parent);
      if (!this.refreshEventReceived) {
            // Apparently not a ConfigurableApplicationContext with refresh support:
            // triggering initial onRefresh manually here.
            onRefresh(wac);
      }
      if (this.publishContext) {
            // Publish the context as a servlet context attribute.
            String attrName = getServletContextAttributeName();
            getServletContext().setAttribute(attrName, wac);
            if (logger.isDebugEnabled()) {
                  logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
                  "' as ServletContext attribute with name [" + attrName + "]");
            }
      }
      return wac;
}
FrameworkServlet.createWebApplicationContext


Java代码 复制代码

protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent)
throws BeansException {
      if (logger.isDebugEnabled()) {
            logger.debug("Servlet with name '" + getServletName() +
            "' will try to create custom WebApplicationContext context of class '" +
            getContextClass().getName() + "'" + ", using parent context [" + parent + "]");
      }
      if (!ConfigurableWebApplicationContext.class.isAssignableFrom(getContextClass())) {
            throw new ApplicationContextException(
            "Fatal initialization error in servlet with name '" + getServletName() +
            "': custom WebApplicationContext class [" + getContextClass().getName() +
            "] is not of type ConfigurableWebApplicationContext");
      }
      ConfigurableWebApplicationContext wac =
      (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(getContextClass());
      wac.setParent(parent);
      wac.setServletContext(getServletContext());
      wac.setServletConfig(getServletConfig());
      wac.setNamespace(getNamespace());
      wac.setConfigLocation(getContextConfigLocation());
      wac.addApplicationListener(new SourceFilteringListener(wac, this));
      postProcessWebApplicationContext(wac);
      wac.refresh();
      return wac;
}


2)初始化策略
   具体与SpringMVC相关的策略在DispatcherServlet中初始化,DispatcherServlet的类定义被加载时,如下初始化代码段被执行:

Java代码 复制代码

static {
      // Load default strategy implementations from properties file.
      // This is currently strictly internal and not meant to be customized
      // by application developers.
      try {
            ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
            defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
      }
      catch (IOException ex) {
            throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage());
      }


   我们可以看到,SpringMVC的策略在与DispatcherServlet同目录的Dispatcher.properties文件中配置,如下是Spring2.5的默认配置策略
Dispatcher.properties 写道

# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.throwaway.ThrowawayControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver


    当然,我们可以变更其处理策略,通过上面部分,我们知道,FrameworkServlet实现了ApplicationListener,并在构建WebApplicationContext后,将自身(this)向WebApplicationContext注册,因此WebApplicationContext初始化完毕之后,将发送ContextRefreshedEvent事件,该事件实际上被DispatcherServlet处理,处理过程如下:

FrameworkServlet.onApplicationEvent –> DispatcherServlet.onRefresh –> DispatcherServlet.initStrategies


    DispatcherServlet.initStrategies代码如下,具体处理过程可参见Spring源代码

Java代码 复制代码

protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);


3.请求处理流程

    SpringMVC的请求处理从doService–>doDispatch为入口,实际上,我们只要紧紧抓住HandlerMapping、HandlerAdapter、ViewResolver这三个核心对象,SpringMVC的一整个运行机制看起来将非常简单,其主要处理流程包括:

1)将请求映射到具体的执行处理链,见如下代码

Java代码 复制代码

// Determine handler for the current request.
mappedHandler = getHandler(processedRequest, false);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
      noHandlerFound(processedRequest, response);
      return;
}


   具体看一下getHandler是如何处理的

Java代码 复制代码

protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {
      HandlerExecutionChain handler =
      (HandlerExecutionChain) request.getAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
      if (handler != null) {
            if (!cache) {
                  request.removeAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
            }
            return handler;
      }
      Iterator it = this.handlerMappings.iterator();
      while (it.hasNext()) {
            HandlerMapping hm = (HandlerMapping) it.next();
            if (logger.isDebugEnabled()) {
                  logger.debug("Testing handler map [" + hm + "] in DispatcherServlet with name '" +
                  getServletName() + "'");
            }
            handler = hm.getHandler(request);
            if (handler != null) {
                  if (cache) {
                        request.setAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE, handler);
                  }
                  return handler;
            }
      }
      return null;


   可以看到,仅仅是遍历每一个HandlerMapping,如果能够其能够处理,则返回执行处理链(HandleExecuteChain)

2)执行处理链的拦截器列表的preHandle方法,如果执行时返回false,表示该拦截器已处理完请求要求停止执行后续的工作,则倒序执行所有已执行过的拦截器的afterCompletion方法,并返回

Java代码 复制代码

// Apply preHandle methods of registered interceptors.
HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
if (interceptors != null) {
      for (int i = 0; i < interceptors.length; i++) {
            HandlerInterceptor interceptor = interceptors[i];
            if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
                  triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
                  return;
            }
            interceptorIndex = i;
      }
}


 3)根据处理对象获得处理器适配器(HandlerAdapter),并由处理适配器负责最终的请求处理,并返回ModelAndView(mv),关于处理器适配器的作用,见第2部分的说明

Java代码 复制代码

// Actually invoke the handler.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());


   具体看getHandlerAdapter如何工作

Java代码 复制代码

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
      Iterator it = this.handlerAdapters.iterator();
      while (it.hasNext()) {
            HandlerAdapter ha = (HandlerAdapter) it.next();
            if (logger.isDebugEnabled()) {
                  logger.debug("Testing handler adapter [" + ha + "]");
            }
            if (ha.supports(handler)) {
                  return ha;
            }
      }
      throw new ServletException("No adapter for handler [" + handler +
      "]: Does your handler implement a supported interface like Controller?");
}


    非常简单,仅仅是依次询问HandlerAdapter列表是否支持处理当前的处理器对象

4)倒序执行处理链拦截器列表的postHandle方法

Java代码 复制代码

// Apply postHandle methods of registered interceptors.
if (interceptors != null) {
      for (int i = interceptors.length - 1; i >= 0; i–) {
            HandlerInterceptor interceptor = interceptors[i];
            interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
      }
}


5)根据ViewResolver获取相应的View实例,并生成视图响应给客户端

Java代码 复制代码

// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
      render(mv, processedRequest, response);
}
else {
if (logger.isDebugEnabled()) {
      logger.debug("Null ModelAndView returned to DispatcherServlet with name '" +
      getServletName() + "': assuming HandlerAdapter completed request handling");
}
}


    再看看render方法

Java代码 复制代码

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)
throws Exception {
      // Determine locale for request and apply it to the response.
      Locale locale = this.localeResolver.resolveLocale(request);
      response.setLocale(locale);
      View view = null;
      if (mv.isReference()) {
            // We need to resolve the view name.
            view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
            if (view == null) {
                  throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
                  "' in servlet with name '" + getServletName() + "'");
            }
      }
      else {
            // No need to lookup: the ModelAndView object contains the actual View object.
            view = mv.getView();
            if (view == null) {
                  throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
                  "View object in servlet with name '" + getServletName() + "'");
            }
      }
      // Delegate to the View object for rendering.
      if (logger.isDebugEnabled()) {
            logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
      }
      view.render(mv.getModelInternal(), request, response);
}


6)倒序执行处理链拦截器列表的afterCompletion方法

Java代码 复制代码

private void triggerAfterCompletion(
HandlerExecutionChain mappedHandler, int interceptorIndex,
HttpServletRequest request, HttpServletResponse response, Exception ex)
throws Exception {
      // Apply afterCompletion methods of registered interceptors.
      if (mappedHandler != null) {
            HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
            if (interceptors != null) {
                  for (int i = interceptorIndex; i >= 0; i–) {
                        HandlerInterceptor interceptor = interceptors[i];
                        try {
                              interceptor.afterCompletion(request, response, mappedHandler.getHandler(), ex);
                        }
                        catch (Throwable ex2) {
                              logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
                        }
                  }
            }
      }
}


--转自 北京联动北方科技有限公司

该贴由koei123转至本版2015-2-5 17:25:19



赞(0)    操作        顶端 
rui.yuan
注册用户
等级:中校
经验:1637
发帖:29
精华:0
注册:1970-1-1
状态:离线
发送短消息息给rui.yuan 加好友    发送短消息息给rui.yuan 发消息
发表于: IP:您无权察看 2015-1-19 9:32:32 | [全部帖] [楼主帖] 2  楼

底层实现还真没去触及。。。。



赞(0)    操作        顶端 
yr
注册用户
等级:少校
经验:1085
发帖:5
精华:0
注册:2015-1-19
状态:离线
发送短消息息给yr 加好友    发送短消息息给yr 发消息
发表于: IP:您无权察看 2015-1-19 17:50:42 | [全部帖] [楼主帖] 3  楼

号高端的样子 北京联动北方科技有限公司



赞(0)    操作        顶端 
running
注册用户
等级:中尉
经验:491
发帖:0
精华:0
注册:2015-1-20
状态:离线
发送短消息息给running 加好友    发送短消息息给running 发消息
发表于: IP:您无权察看 2015-1-21 23:13:26 | [全部帖] [楼主帖] 4  楼

北京联动北方科技有限公司



赞(0)    操作        顶端 
hei_nihao
注册用户
等级:少校
经验:1279
发帖:19
精华:0
注册:2015-4-17
状态:离线
发送短消息息给hei_nihao 加好友    发送短消息息给hei_nihao 发消息
发表于: IP:您无权察看 2015-4-17 10:02:58 | [全部帖] [楼主帖] 5  楼

北京联动北方科技有限公司,看不懂额,菜鸟飘过...


赞(0)    操作        顶端 
总帖数
5
每页帖数
101/1页1
返回列表
发新帖子
请输入验证码: 点击刷新验证码
您需要登录后才可以回帖 登录 | 注册
技术讨论