springMVC源码浅析

本文涉及到代码比较多,建议在电脑端阅读!!!

借用网上的一张springMVC流程图,

spingMVC源码分三部分来分析

1:ApplicationContext初始化时建立所有url和controller类的对应关系(使用map保存)

2:根据请求url找到对应的controller,并从controller中找到处理请求的方法

3:request参数绑定到方法的形参,执行方法并处理请求,最后返回结果

第一步:建立Map<urls,Controller>关系

我们首先看第一个步骤,也就是建立Map<url,controller>关系的部分.

第一部分的入口类为

ApplicationObjectSupport

--->setApplicationContext()

-->initApplicationContext(context),实现子类AbstractDetectingUrlHandlerMapping实现了该方法,所以我们直接看子类中的初始化容器


建立当前ApplicationContext中的所有controller和url的对应关系


 /**

*获取controller中所有方法的url,由子类实现,典型的模板模式

 **/

protected abstract String[] determineUrlsForHandler(String beanName);

determineUrlsForHandler(String beanName)方法的作用是获取每个controller中的url,不同的子类有不同的实现,这是一个典型的模板设计模式(前面有一篇文章有讲到模板模式),

DefaultAnnotationHandlerMapping是AbstractDetectingUrlHandlerMapping的子类,处理注解形式的url映射.所以我们这里以DefaultAnnotationHandlerMapping来进行分析,

接下来看看DefaultAnnotationHandlerMapping是如何查beanName上所有映射的url.

/**

   * 获取controller中所有的url

     */

  protected String[] determineUrlsForHandler(String beanName) {

       // 获取ApplicationContext容器

    ApplicationContext context = getApplicationContext();

        //从容器中获取controller

     Class<?> handlerType = context.getType(beanName);

     // 获取controller上的@RequestMapping注解

        RequestMapping mapping = context.findAnnotationOnBean(beanName, RequestMapping.class);

        if (mapping != null) { // controller上有注解

            this.cachedMappings.put(handlerType, mapping);

        // 返回结果集

            Set<String> urls = new LinkedHashSet<String>();

        // controller的映射url

            String[] typeLevelPatterns = mapping.value();

            if (typeLevelPatterns.length > 0) { // url>0

                // 获取controller中所有方法及方法的映射url

                String[] methodLevelPatterns = determineUrlsForHandlerMethods(handlerType, true);

                for (String typeLevelPattern : typeLevelPatterns) {

                    if (!typeLevelPattern.startsWith("/")) {

                        typeLevelPattern = "/" + typeLevelPattern;

                    }

                    boolean hasEmptyMethodLevelMappings = false;

                    for (String methodLevelPattern : methodLevelPatterns) {

                        if (methodLevelPattern == null) {

                            hasEmptyMethodLevelMappings = true;

                        } else {

                // controller的映射url+方法映射的url

                       String combinedPattern = getPathMatcher().combine(typeLevelPattern, methodLevelPattern);

                // 保存到set集合中                 addUrlsForPath(urls, combinedPattern);

                        }

                    }

                    if (hasEmptyMethodLevelMappings ||

                            org.springframework.web.servlet.mvc.Controller.class.isAssignableFrom(handlerType)) {

                        addUrlsForPath(urls, typeLevelPattern);

                    }

                }

         // 以数组形式返回controller上的所有url

                return StringUtils.toStringArray(urls);

            }

            else {

                // controller上的@RequestMapping映射url为空串,直接找方法的映射url

                return determineUrlsForHandlerMethods(handlerType, false);

            }

        // controller上没@RequestMapping注解

        }  else if (AnnotationUtils.findAnnotation(handlerType, Controller.class) != null) {

            // 获取controller中方法上的映射url

            return determineUrlsForHandlerMethods(handlerType, false);

        }

        else {

            return null;

        }

}

上面的方式返回该controller中的所有url,

接下来把urls和controller放在handlerMap中

遍历式存放for()循环


//具体存放的方法

protected void registerHandler(String urlPath, Object handler) {// Eagerly resolve handler if referencing singleton via name.

//如果通过名称引用单体,则急于解析处理程序。(感觉翻译的不很是准确) if (!this.lazyInitHandlers && handler instanceof String) {if (getApplicationContext().isSingleton(handlerName)) {this.handlerMap.get(urlPath);

//如果存在重复的url,则报异常IllegalStateExceptionif (mappedHandler != null) {if (mappedHandler != resolvedHandler) { throw new IllegalStateException("Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +"]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");else {//否则url校验if (urlPath.equals("/")) {  else if (urlPath.equals("/*")) { else {

//放到handlerMap中this.handlerMap.put(urlPath, resolvedHandler);

其中存放url和controller的map:

private final Map<String, Object> handlerMap new LinkedHashMap<String, Object>();

看完以上源码咱们可以知道Map<urls,Controller>关系以及建立好了(HandlerMapping关系)

第二步:通过请求的Url查找需要访问的controller

前面咱们已经讲过了DispatcherServlet为核心代码(入口),轻重黑心的方法为doService()

,另外doService()中的核心逻辑由doDispatch()实现,我们查看doDispatch()的源代码.

org.springframework.web.servlet.DispatcherServlet

/** * 中央控制器,控制请求的转发  */ protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {null;int interceptorIndex = -1;try {boolean errorView = false;try {// 1.检查是否是文件上传的请求                processedRequest = checkMultipart(request);// 2.取得处理当前请求的controller,这里也称为hanlder,处理器,第一个步骤的意义就在这里体现了.这里并不是直接返    

controller,而是返回的HandlerExecutionChain请求处理器链对象,该对象封装了handlerinterceptors.                mappedHandler = getHandler(processedRequest, false);// 如果handler为空,则返回404                if (mappedHandler == null || mappedHandler.getHandler() == null) {return;//3. 获取处理request的处理器适配器handler adapter                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// 处理 last-modified 请求头                String method = request.getMethod();boolean isGet = "GET".equals(method);if(isGet || "HEAD".equals(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());urlPathHelper.getRequestUri(request);if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;// 4.拦截器的预处理方法                HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();if (interceptors != null) {for (int i = 0; i < interceptors.length; i++) {if(!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {null);return;// 5.实际的处理器处理请求,返回结果视图对象                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());//结果视图对象的处理                if (mv != null && !mv.hasView()) {// 6.拦截器的后处理方法                if (interceptors != null) {for (int i = interceptors.length - 1; i >= 0; i--) {catch (ModelAndViewDefiningException ex) {catch (Exception ex) {null ? mappedHandler.getHandler() : null);null);if (mv != null && !mv.wasCleared()) {if (errorView) {clearErrorRequestAttributes(request);else {/* logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName()                             "': assuming HandlerAdapter completed request handling");*/            }//请求成功响应之后的方法            triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);catch (Exception e){//            logger.error("errpr".e);        }

上面getHandler(processedRequest, false)方法:


本方法方法实际上就是从HandlerMapping中找到url和controller的对应关系.这也就是第一个步骤:建立Map<url,Controller>的意义.我们知道,最终处理request的是controller中的方法,我们现在只是知道了controller,还要进一步确认controller中处理request的方法.由于下面的步骤和第三个步骤关系更加紧密,直接转到第三个步骤.

第三步:反射调用请求处理的方法,并返回视图结果

上面的doDispatch()方法中,第2步其实就是从第一个步骤中的Map<urls,beanName>中取得controller,然后经过拦截器的预处理方法,到最核心的部分--第5步调用controller的方法处理请求.

在第2步中我们已经知道了处理request的Controller类,第5步就是要根据url确定controller中处理请求的方法,然后通过反射获取该方法上的注解和参数,解析方法和参数上的注解,最后反射调用方法获取ModelAndView结果视图。

因为上面采用注解url形式说明的,所以我们这里继续以注解处理器适配器来说明,

第5步调用的就是AnnotationMethodHandlerAdapter的handle(),该方法中的核心逻辑由invokeHandlerMethod(request, response, handler)实现。

/**

 *获取处理请求的方法,执行并返回结果视图

*/

protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {

      // 1.获取方法解析器

      ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);

      // 2.解析request中的url,获取处理request的方法

      Method handlerMethod = methodResolver.resolveHandlerMethod(request);

      // 3.方法调用器

ServletHandlerMethodInvoker methodInvoker=new ServletHandlerMethodInvoker(methodResolver);

        ServletWebRequest webRequest = new ServletWebRequest(request, response);

        ExtendedModelMap implicitModel = new BindingAwareModelMap();

      // 4.执行方法

    Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);

       // 5.封装结果视图

   ModelAndView mav =methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);

    methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);

   return mav;

 }

这一部分的核心就在2和4了.先看第2步,通过request找controller的处理方法.实际上就是拼接controller的url和方法的url,与request的url进行匹配,找到匹配的方法.

自此代码就回到了咱们的controller中的方法返回类型ModelAndView了


猜你喜欢

转载自blog.51cto.com/10983206/2564032
今日推荐