SpringMVC中处理Http请求的原理详解

 更新时间:2023年12月02日 08:54:07   作者:nuomizhende45  
这篇文章主要介绍了SpringMVC中处理Http请求的原理详解,当一个http请求过来了首先经过的是DispatcherServlet这么一个前端控制器并调用了这个前端控制器的doService方法,这个方法最终我们发现它调用了doDispatcher这么一个方法,需要的朋友可以参考下

SpingMVC处理Http请求原理

当一个http请求过来了首先经过的是DispatcherServlet这么一个前端控制器并调用了这个前端控制器的doService方法。

这个方法最终我们发现它调用了doDispatcher这么一个方法。这就是SpringMVC处理http请求的入口了。

   protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        if(this.logger.isDebugEnabled()) {
            String attributesSnapshot = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult()?" resumed":"";
            this.logger.debug("DispatcherServlet with name \'" + this.getServletName() + "\'" + attributesSnapshot + " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
        }
        HashMap attributesSnapshot1 = null;
        if(WebUtils.isIncludeRequest(request)) {
            attributesSnapshot1 = new HashMap();
            Enumeration inputFlashMap = request.getAttributeNames();
            label112:
            while(true) {
                String attrName;
                do {
                    if(!inputFlashMap.hasMoreElements()) {
                        break label112;
                    }
                    attrName = (String)inputFlashMap.nextElement();
                } while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));
                attributesSnapshot1.put(attrName, request.getAttribute(attrName));
            }
        }
        request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());
        request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
        request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
        request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource());
        if(this.flashMapManager != null) {
            FlashMap inputFlashMap1 = this.flashMapManager.retrieveAndUpdate(request, response);
            if(inputFlashMap1 != null) {
                request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap1));
            }
            request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
            request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
        }
        try {
            this.doDispatch(request, response);
        } finally {
            if(!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot1 != null) {
                this.restoreAttributesAfterInclude(request, attributesSnapshot1);
            }
        }
    }

这个方法里面我们发现定义了一个ModelAndView将要返回的视图与数据,并且首先检测这个请求是不是一个上传的请求,然后根据这个请求获取对应的Handler,如果Handler不为空的话就根据这个handler获取对应的HandlerAdapter。接着调用拦截器链的所有前置拦截的这么一个方法,接着调用adapter的真正的处理方法处理请求,最后调用拦截器链中的所有后置拦截方法。

  protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        try {
            try {
                //定义一个空的ModelAndView
                ModelAndView err = null;
                Object dispatchException = null;
                try {
                //检查是否是上传请求
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                //获取handler
                    mappedHandler = this.getHandler(processedRequest);
                    if(mappedHandler == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }
                //根据handler获取handlerAdapter
                    HandlerAdapter err1 = this.getHandlerAdapter(mappedHandler.getHandler());
                    String method = request.getMethod();
                    boolean isGet = "GET".equals(method);
                    if(isGet || "HEAD".equals(method)) {
                        long lastModified = err1.getLastModified(request, mappedHandler.getHandler());
                        if(this.logger.isDebugEnabled()) {
                            this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                        }
                        if((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }
                //前置拦截
                    if(!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }
                //真正处理
                    err = err1.handle(processedRequest, response, mappedHandler.getHandler());
                    if(asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }
                    this.applyDefaultViewName(processedRequest, err);
                //后置拦截
                    mappedHandler.applyPostHandle(processedRequest, response, err);
                } 
                //后面一些catch finally语句省略。。。。。

继续跟进这个getHandler方法看看具体是怎么实现的,发现就是循环遍历最开始初始化DispatcherServlet的时候初始化的那7个handlerMapping,去调用这些handlerMapping的getHandler方法;这里我们主要看这个RequestMappingHandlerMapping。

 跟进发现是调用了RequestMappingHandlerMapping的父类AbstractHandlerMapping中的getHandler方法,并且这个方法又是去调用了getHandlerInternal来获取Handler的。

继续跟进这个getHandlerInternal方法,首先它根据这个request获取它的URI,接着通过uri获取对应的HandlerMethod对象最终返回了。

    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        String lookupPath = this.getUrlPathHelper().getLookupPathForRequest(request);
        if(this.logger.isDebugEnabled()) {
            this.logger.debug("Looking up handler method for path " + lookupPath);
        }
        this.mappingRegistry.acquireReadLock();
        HandlerMethod var4;
        try {
            HandlerMethod handlerMethod = this.lookupHandlerMethod(lookupPath, request);
            if(this.logger.isDebugEnabled()) {
                if(handlerMethod != null) {
                    this.logger.debug("Returning handler method [" + handlerMethod + "]");
                } else {
                    this.logger.debug("Did not find handler method for [" + lookupPath + "]");
                }
            }
            var4 = handlerMethod != null?handlerMethod.createWithResolvedBean():null;
        } finally {
            this.mappingRegistry.releaseReadLock();
        }
        return var4;
    }

 跟进这个getLookupPathForRequest发现又调用了getPathWithinServletMapping方法。

 这里我们就知道了这个lookupPath得到的就是这个请求的URI,拿到了这个URI接着就去调用lookupHandlerMethod方法了。

这里lookupHandlerMethod方法顾名思义就是通过这个URI根据请求的URI去寻找对应的HandlerMethod。

    protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        ArrayList matches = new ArrayList();
        //通过这个uri去Map中查询与那个HandlerMethod映射
        List directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
        if(directPathMatches != null) {
            this.addMatchingMappings(directPathMatches, matches, request);
        }
        if(matches.isEmpty()) {
            this.addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
        }
        //若查询出这个uri有对应的映射关系
        if(!matches.isEmpty()) {
            AbstractHandlerMethodMapping.MatchComparator comparator = new AbstractHandlerMethodMapping.MatchComparator(this.getMappingComparator(request));
           //对映射关系排序
            matches.sort(comparator);
            if(this.logger.isTraceEnabled()) {
                this.logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches);
            }
            //获取排序后第一个HandlerMethod
            AbstractHandlerMethodMapping.Match bestMatch = (AbstractHandlerMethodMapping.Match)matches.get(0);
            if(matches.size() > 1) {
                if(CorsUtils.isPreFlightRequest(request)) {
                    return PREFLIGHT_AMBIGUOUS_MATCH;
                }
                AbstractHandlerMethodMapping.Match secondBestMatch = (AbstractHandlerMethodMapping.Match)matches.get(1);
                if(comparator.compare(bestMatch, secondBestMatch) == 0) {
                    Method m1 = bestMatch.handlerMethod.getMethod();
                    Method m2 = secondBestMatch.handlerMethod.getMethod();
                    throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path \'" + request.getRequestURL() + "\': {" + m1 + ", " + m2 + "}");
                }
            }
            this.handleMatch(bestMatch.mapping, lookupPath, request);
            return bestMatch.handlerMethod;
        } else {
            return this.handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
        }
    }

最终得到了这么一个包含了Handler与这个URI具体处理方法的一个HandlerMethod对象。

注意现在这个handler可能还没有被创建出来或者说没有得到,只是知道它的beanName。

最终还要调用这么一个createWithResolvedBean方法来得到。

 获得了这个handler之后,又继续根据这个handler获得了HandlerExecutionChain。

   public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        Object handler = this.getHandlerInternal(request);
        if(handler == null) {
            handler = this.getDefaultHandler();
        }
        if(handler == null) {
            return null;
        } else {
            if(handler instanceof String) {
                String executionChain = (String)handler;
                handler = this.obtainApplicationContext().getBean(executionChain);
            }
            HandlerExecutionChain executionChain1 = this.getHandlerExecutionChain(handler, request);
            if(CorsUtils.isCorsRequest(request)) {
                CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
                CorsConfiguration handlerConfig = this.getCorsConfiguration(handler, request);
                CorsConfiguration config = globalConfig != null?globalConfig.combine(handlerConfig):handlerConfig;
                executionChain1 = this.getCorsHandlerExecutionChain(request, executionChain1, config);
            }
            return executionChain1;
        }
    }

所以得出最终结论getHandler这个方法最终所有通过这个URI返回了一个最开始注册的handler,然后通过 这个handler返回了一个包含这这个HandlerMethod以及一个拦截器链的这么一个对象。之后执行前置拦截器,handler方法,后置拦截,完成。

到此这篇关于SpingMVC中处理Http请求的原理详解的文章就介绍到这了,更多相关SpingMVC处理Http请求内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 一篇文章带你了解Java SpringMVC返回null

    一篇文章带你了解Java SpringMVC返回null

    这篇文章主要介绍了Spring MVC返回null,文中讲的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2021-10-10
  • Java自动拆箱空指针异常的解决

    Java自动拆箱空指针异常的解决

    这篇文章主要介绍了Java自动拆箱空指针异常的解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • 解决idea配置Tomcat Deployment没有artifact选项的问题

    解决idea配置Tomcat Deployment没有artifact选项的问题

    今天在配置的时候tomcat deployment中却找不到artifact,没有artifact就不能打成war包上传到服务器了,那么怎么解决没有artifact选项的问题呢,今天通过本文给大家分享idea配置Tomcat Deployment没有artifact选项的解决方案,一起看看吧
    2023-10-10
  • Mybatis-plus中IService接口的基本使用步骤

    Mybatis-plus中IService接口的基本使用步骤

    Mybatis-plus是一个Mybatis的增强工具,它提供了很多便捷的方法来简化开发,IService是Mybatis-plus提供的通用service接口,封装了常用的数据库操作方法,包括增删改查等,下面这篇文章主要给大家介绍了关于Mybatis-plus中IService接口的基本使用步骤,需要的朋友可以参考下
    2023-06-06
  • SpringBoot中实现启动任务的实现步骤

    SpringBoot中实现启动任务的实现步骤

    这篇文章主要介绍了SpringBoot中实现启动任务的实现步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • SpringBoot 自定义starter yaml提示失效问题及解决方法

    SpringBoot 自定义starter yaml提示失效问题及解决方法

    在自定义starter后,必不可少会有properties配置参数需要指定,而在有时又不知道为什么出现这个问题,这篇文章主要介绍了SpringBoot 自定义starter yaml提示失效问题,需要的朋友可以参考下
    2022-12-12
  • Mybatis Plus整合PageHelper分页的实现示例

    Mybatis Plus整合PageHelper分页的实现示例

    这篇文章主要介绍了Mybatis Plus整合PageHelper分页的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • IDEA使用Maven创建module出现Ignored pom.xml问题及解决

    IDEA使用Maven创建module出现Ignored pom.xml问题及解决

    这篇文章主要介绍了IDEA使用Maven创建module出现Ignored pom.xml问题及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • Springboot启用多个监听端口代码实例

    Springboot启用多个监听端口代码实例

    这篇文章主要介绍了Springboot启用多个监听端口代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-06-06
  • 详解Kotlin和anko融合进行Android开发

    详解Kotlin和anko融合进行Android开发

    本篇文章主要介绍了Kotlin和anko融合进行Android开发,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-11-11

最新评论