栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

2021-11-19学习记录 浅析SpringBoot环境下DispatcherServlet执行分发的过程

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

2021-11-19学习记录 浅析SpringBoot环境下DispatcherServlet执行分发的过程

先准备测试需要的控制器类

@Controller
public class HelloController {
    @RequestMapping("/hello")
    @ResponseBody
    public String hello(){
        return "Hello Spring!";
    }
}

来看DispatcherServlet的类结构:

它继承了frameworkServlet,我们都知道在浏览器上发出请求,最先到达的是Servlet的doGet()或doPost方法。 

DispatcherServlet本身并没有实现这两个方法,于是去他的父类中查找,

找到了对应的方法,它又调用了processRequest的方法。

    protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        long startTime = System.currentTimeMillis();
        Throwable failureCause = null;
        LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
        LocaleContext localeContext = this.buildLocaleContext(request);
        RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes);
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.registerCallableInterceptor(frameworkServlet.class.getName(), new frameworkServlet.RequestBindingInterceptor());
        this.initContextHolders(request, localeContext, requestAttributes);

        try {
            this.doService(request, response);
        } catch (IOException | ServletException var16) {
            failureCause = var16;
            throw var16;
        } catch (Throwable var17) {
            failureCause = var17;
            throw new NestedServletException("Request processing failed", var17);
        } finally {
            this.resetContextHolders(request, previousLocaleContext, previousAttributes);
            if (requestAttributes != null) {
                requestAttributes.requestCompleted();
            }

            this.logResult(request, response, (Throwable)failureCause, asyncManager);
            this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause);
        }

    }

 在processRequest方法中,进行了一堆初始化操作,最后调用doService方法。

frameworkServlet本实的doService是抽象的,DispatcherServlet实现类它。

 protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        this.logRequest(request);
......
        RequestPath previousRequestPath = null;
        if (this.parseRequestPath) {
            previousRequestPath = (RequestPath)request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);
            ServletRequestPathUtils.parseAndCache(request);
        }

        try {
            this.doDispatch(request, response);
        } finally {
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
                this.restoreAttributesAfterInclude(request, attributesSnapshot);
            }

            if (this.parseRequestPath) {
                ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
            }

        }

    }

在doService方法里,先进行初始化,设置了一堆参数。之后来到了核心语句:

doDispatch

{
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            try {
                ModelAndView mv = null;
                Object dispatchException = null;

                try {
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                    mappedHandler = this.getHandler(processedRequest);
                    if (mappedHandler == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }

                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                    String method = request.getMethod();
                    boolean isGet = HttpMethod.GET.matches(method);
                    if (isGet || HttpMethod.HEAD.matches(method)) {
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                        if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }

                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }

                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                   ......

在doDisptach中,我们看到它实例化了一个ModelAndView对象,我们已经知道SpringMVC最终会将我们模型和视图对象封装程ModelAndView对象进行解析。也就是说,分发工作在实例化mv对象之前就已经完成了。

方法中,他还创建了一个mapperHandler对象,从名字来看是映射执行器的意思,通过debug我们发现它是这个样子的

内部包含了我们要执行的方法。所以mapperHandler的创建与初始化就是执行分发的过程。

来到获得mapperHandler的get方法

   protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        if (this.handlerMappings != null) {
            Iterator var2 = this.handlerMappings.iterator();

            while(var2.hasNext()) {
                HandlerMapping mapping = (HandlerMapping)var2.next();
                HandlerExecutionChain handler = mapping.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
        }

        return null;
    }

 可以看到它会遍历控制器映射,如果不是空的话,就直接返回。先来看一下控制器映射中都有什么。我们首先看到了RequestMappingHandlerMapper,请求映射处理映射。从代码中我们可以看到,最终是将RequestMappingHandlerMapper解析成了控制器执行链。不难猜测,这就是执行对应方法的对象。那么如何获得对应的方法呢。

来到getHandler()方法

 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 handlerName = (String)handler;
                handler = this.obtainApplicationContext().getBean(handlerName);
            }

            if (!ServletRequestPathUtils.hasCachedPath(request)) {
                this.initLookupPath(request);
            }

            HandlerExecutionChain executionChain = this.getHandlerExecutionChain(handler, request);
.......
            return executionChain;
        }
    }

在方法开始就调用了另外一个方法来获取Object类型的handler,接着往下进入

    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        request.removeAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);

        HandlerMethod var2;
        try {
            var2 = super.getHandlerInternal(request);
        } finally {
            ProducesRequestCondition.clearMediaTypesAttribute(request);
        }

        return var2;
    }

我们可以看到最终返回了一个HanlerMethod,而他是通过调用父类的同名方法得到的。

来到父类的该方法

    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        String lookupPath = this.initLookupPath(request);
        this.mappingRegistry.acquireReadLock();

        HandlerMethod var4;
        try {
            HandlerMethod handlerMethod = this.lookupHandlerMethod(lookupPath, request);
            var4 = handlerMethod != null ? handlerMethod.createWithResolvedBean() : null;
        } finally {
            this.mappingRegistry.releaseReadLock();
        }

        return var4;
    }

它首先获取了一个叫"lookupPath"目标路径的字符串对象。通过debug我们可以看到整个lookupPath正是我们的请求路径。

然后调用另一个方法来返回控制器方法

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        List.Match> matches = new ArrayList();
        List directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
        if (directPathMatches != null) {
            this.addMatchingMappings(directPathMatches, matches, request);
        }

        if (matches.isEmpty()) {
            this.addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
        }

        if (matches.isEmpty()) {
            return this.handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
        } else {
            AbstractHandlerMethodMapping.Match bestMatch = (AbstractHandlerMethodMapping.Match)matches.get(0);
            if (matches.size() > 1) {
                Comparator.Match> comparator = new AbstractHandlerMethodMapping.MatchComparator(this.getMappingComparator(request));
                matches.sort(comparator);
                bestMatch = (AbstractHandlerMethodMapping.Match)matches.get(0);
                if (this.logger.isTraceEnabled()) {
                    this.logger.trace(matches.size() + " matching mappings: " + matches);
                }

                if (CorsUtils.isPreFlightRequest(request)) {
                    Iterator var7 = matches.iterator();

                    while(var7.hasNext()) {
                        AbstractHandlerMethodMapping.Match match = (AbstractHandlerMethodMapping.Match)var7.next();
                        if (match.hasCorsConfig()) {
                            return PREFLIGHT_AMBIGUOUS_MATCH;
                        }
                    }
                } else {
                    AbstractHandlerMethodMapping.Match secondBestMatch = (AbstractHandlerMethodMapping.Match)matches.get(1);
                    if (comparator.compare(bestMatch, secondBestMatch) == 0) {
                        Method m1 = bestMatch.getHandlerMethod().getMethod();
                        Method m2 = secondBestMatch.getHandlerMethod().getMethod();
                        String uri = request.getRequestURI();
                        throw new IllegalStateException("Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
                    }
                }
            }

            request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
            this.handleMatch(bestMatch.mapping, lookupPath, request);
            return bestMatch.getHandlerMethod();
        }
    }

 

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        List.Match> matches = new ArrayList();
        List directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
        if (directPathMatches != null) {
            this.addMatchingMappings(directPathMatches, matches, request);
        }

        if (matches.isEmpty()) {
            this.addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
        }

        if (matches.isEmpty()) {
            return this.handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
        } else {
            AbstractHandlerMethodMapping.Match bestMatch = (AbstractHandlerMethodMapping.Match)matches.get(0);
            if (matches.size() > 1) {
                Comparator.Match> comparator = new AbstractHandlerMethodMapping.MatchComparator(this.getMappingComparator(request));
                matches.sort(comparator);
                bestMatch = (AbstractHandlerMethodMapping.Match)matches.get(0);
                if (this.logger.isTraceEnabled()) {
                    this.logger.trace(matches.size() + " matching mappings: " + matches);
                }

                if (CorsUtils.isPreFlightRequest(request)) {
                    Iterator var7 = matches.iterator();

                    while(var7.hasNext()) {
                        AbstractHandlerMethodMapping.Match match = (AbstractHandlerMethodMapping.Match)var7.next();
                        if (match.hasCorsConfig()) {
                            return PREFLIGHT_AMBIGUOUS_MATCH;
                        }
                    }
                } else {
                    AbstractHandlerMethodMapping.Match secondBestMatch = (AbstractHandlerMethodMapping.Match)matches.get(1);
                    if (comparator.compare(bestMatch, secondBestMatch) == 0) {
                        Method m1 = bestMatch.getHandlerMethod().getMethod();
                        Method m2 = secondBestMatch.getHandlerMethod().getMethod();
                        String uri = request.getRequestURI();
                        throw new IllegalStateException("Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
                    }
                }
            }

            request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
            this.handleMatch(bestMatch.mapping, lookupPath, request);
            return bestMatch.getHandlerMethod();
        }
    }

首先从映射注册器里获得了直接匹配对象,最后经过一系列判断,嘴后调用了一个getHandlerMethod方法获得了控制器方法。

  public HandlerMethod getHandlerMethod() {
            return this.registration.getHandlerMethod();
        }

在HandlerMethod类中我们可以看到很多与注解有关的方法

 猜测他可能是获得与注解匹配的值后,利用反射来获得我们请求路径对应方法。并加入到执行链中。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/571325.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号