10.SpringBoot2请求处理-【源码分析】-请求映射原理

在这里插入图片描述

 SpringMVC功能分析都从 org.springframework.web.servlet.DispatcherServlet -> doDispatch()开始

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

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

        try {
            processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);

            // 找到当前请求使用哪个Handler(Controller的方法)处理
            mappedHandler = getHandler(processedRequest);

            //HandlerMapping:处理器映射。/xxx->>xxxx
    ...
}

 getHandler()方法如下:

@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
        for (HandlerMapping mapping : this.handlerMappings) {
            HandlerExecutionChain handler = mapping.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
    }
    return null;
}

this.handlerMappings用于处理是哪一类请求

RequestMappingHandlerMapping在mappingRegistry中保存了所有@requestMapping和handler的映射。

遍历第一个HandlerMapping(RequestMappingHandlerMapping),发现它有HelloController中所有的请求,最终可以找到是哪个请求,可以用controller的哪个方法进行处理

查看mapping.getHandler(request):

 @Nullable
    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);
            if (this.logger.isTraceEnabled()) {
                this.logger.trace("Mapped to " + handler);
            } else if (this.logger.isDebugEnabled() && !DispatcherType.ASYNC.equals(request.getDispatcherType())) {
                this.logger.debug("Mapped to " + executionChain.getHandler());
            }

            if (this.hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
                CorsConfiguration config = this.getCorsConfiguration(handler, request);
                if (this.getCorsConfigurationSource() != null) {
                    CorsConfiguration globalConfig = this.getCorsConfigurationSource().getCorsConfiguration(request);
                    config = globalConfig != null ? globalConfig.combine(config) : config;
                }

                if (config != null) {
                    config.validateAllowCredentials();
                }

                executionChain = this.getCorsHandlerExecutionChain(request, executionChain, config);
            }

            return executionChain;
        }
    }

进入this.getHandlerInternal()中,

    @Nullable
    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;
    }

可以看到,它调用了父类的super.getHandlerInternal(request),

所以进入父类 AbstractHandlerMethodMapping中:

@Nullable
    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;
    }

开始,它首先获取这个请求路径,看这个请求有没有并发,如果没有的话,就进入lookupHandlerMethod方法:

@Nullable
    protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        List<AbstractHandlerMethodMapping<T>.Match> matches = new ArrayList();
        List<T> 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<T>.Match bestMatch = (AbstractHandlerMethodMapping.Match)matches.get(0);
            if (matches.size() > 1) {
                Comparator<AbstractHandlerMethodMapping<T>.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<T>.Match match = (AbstractHandlerMethodMapping.Match)var7.next();
                        if (match.hasCorsConfig()) {
                            return PREFLIGHT_AMBIGUOUS_MATCH;
                        }
                    }
                } else {
                    AbstractHandlerMethodMapping<T>.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();
        }
    }

1.首先将请求路径lookupPath封装为一个list:directPathMatches

2.如果directPathMatches不为空,则matchs会在addMatchingMappings方法添加该directPathMatches,

addMatchingMappings方法:

	private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
		for (T mapping : mappings) {
			T match = getMatchingMapping(mapping, request);
			if (match != null) {
				matches.add(new Match(match, this.mappingRegistry.getRegistrations().get(mapping)));
			}
		}
	}

通过this.mappingRegistry.getRegistrations().get(mapping)获得处理这个请求的方法

并且matches.size() > 1,也就是相同的接口名,但是可能是put,delete,post方法,也就是方法类型不同,它会默认第一个方法是最适合的,进行返回

所以lookupHandlerMethod方法获得的是对应前端调用的这个接口

所有的请求映射都在HandlerMapping中:

SpringBoot自动配置欢迎页的 WelcomePageHandlerMapping 。访问 /能访问到index.html;

@Bean
        public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
            WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext), applicationContext, this.getWelcomePage(), this.mvcProperties.getStaticPathPattern());
            welcomePageHandlerMapping.setInterceptors(this.getInterceptors(mvcConversionService, mvcResourceUrlProvider));
            welcomePageHandlerMapping.setCorsConfigurations(this.getCorsConfigurations());
            return welcomePageHandlerMapping;
        }

SpringBoot自动配置了默认 的 RequestMappingHandlerMapping

@Bean
        @Primary
        public RequestMappingHandlerMapping requestMappingHandlerMapping(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager, @Qualifier("mvcConversionService") FormattingConversionService conversionService, @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
            return super.requestMappingHandlerMapping(contentNegotiationManager, conversionService, resourceUrlProvider);
        }

总结:

请求进来,挨个尝试所有的HandlerMapping看是否有请求信息。

如果有就找到这个请求对应的handler
如果没有就是下一个 HandlerMapping
我们需要一些自定义的映射处理,我们也可以自己给容器中放HandlerMapping。自定义 HandlerMapping
 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: SpringBoot 家教服务平台源码是一个使用 SpringBoot 框架实现的家教服务平台的开源项目。该平台可以让学生们在该平台上寻找到合适的家教老师,同时让老师们在此平台上发布自己的教学信息,以寻找到适合自己的学生群体。 该平台的主要特点是使用了 SpringBoot 框架,使得开发变得简单、高效。同时,该平台还使用了许多其他的技术,如 MyBatis、Thymeleaf 等,以实现更加优秀的功能。 该平台的源码非常易于理解和使用,基本上每个功能都有一个相应的代码实现,非常方便开发者进行修改或扩展。同时,该平台还提供了详细的使用文档和说明,使得即使是刚刚接触这个平台的开发者也能够很快地上手。 总之,SpringBoot 家教服务平台源码是一个非常优秀、易于使用和扩展的家教服务平台的开源项目,值得开发者们借鉴和参考。 ### 回答2: Spring Boot是一个用于开发和构建基于Spring框架的应用程序的开源框架。它提供了一个快速开发应用程序的环境,可以轻松地构建出高效、灵活和可扩展的Web应用程序。家教服务平台是一个很好的应用场景,它可以利用Spring Boot框架的特点来快速搭建一个高效、稳定的家教服务平台。 从源代码的角度来看,Spring Boot家教服务平台源码涵盖了很多关键技术,如Spring MVC框架、Hibernate ORM框架、MyBatis框架等,还有其他相关的技术如微服务、消息队列、数据库集群等。这些技术共同构建了一个强大的家教服务平台,使其能够实现各种家教服务的需求。 当我们分析家教服务平台的源代码时,我们会发现它的设计非常的优秀、模块化、可扩展性强。整个应用程序是分层架构设计的,视图层、控制层和业务逻辑层之间相互独立,耦合度低,易于维护。源代码中也非常注重代码的可读性和可维护性,采用了代码注释、命名规范、代码风格统一等技术,使得开发人员容易理解和维护源代码。 总之,Spring Boot家教服务平台的源代码是一个非常优秀的应用程序源代码,它不仅整洁、易于理解,而且采用了目前最先进和最流行的后端开发技术,包括了对Java多种关键技术的应用,将来可以作为学习和实践Spring Boot框架的佳例。 ### 回答3: Spring Boot是一种基于Spring框架的轻量级、易扩展的Java Web开发框架,该框架具有高度集成、易于开发、易于部署等特点,并且通过自动化配置,极大地简化了开发工作。家教服务平台是指提供家教服务的在线平台,其主要功能包括学生与教师的注册、教师的验证、课程的发布、学生的评价等等。 Spring Boot家教服务平台源码则是基于Spring Boot框架开发的家教服务平台的源代码。该源码包含了平台的各种功能实现,通过阅读源码可以了解平台的设计和实现思路,方便开发者进行二次开发和定制。 Spring Boot家教服务平台源码通常包括以下内容: 1. 依赖管理文件:定义了项目所需的依赖库信息,例如Spring Boot、MyBatis、Thymeleaf等。 2. 配置文件:包括了应用程序的配置信息,例如数据库连接、应用端口号、邮件服务器等。 3. 控制器:负责处理用户请求,驱动服务逻辑,生成相应的响应结果。 4. 服务层:包括业务逻辑和数据访问逻辑,通过接口和实现类的方式提供服务。 5. 实体类:包括了数据库表的映射实体类,用于实现ORM数据库访问。 通过阅读Spring Boot家教服务平台源码,开发者可以深入了解Spring Boot框架的开发思想和最佳实践,同时学习实际的应用场景和解决方案,提高自己的开发水平和项目实施能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值