springboot请求映射原理(一)——接口路径扫如何加载内存中

请求映射原理

springboot启动过程中,扫描所有@RestController @Controller注解定义类,将含有请求注解如@GetMapping、@PostMapping注解存放到一个map(Map<T, MappingRegistration>)中,通过请求接口地址,获取MappingRegistration,调用具体的接口类执行

springboot是如何加载映射器到容器的?

通过自动配置类WebMvcAutoConfiguration,默认使用RequestMappingHandlerMapping解析接口地址,WebMvcAutoConfiguration在spring.fatories中有定义
spring-boot-aotuconfigure中的spring.factories

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

然后执行父类方法,构造RequestMappingHandlerMapping 作为默认的映射处理器

	@Bean
	@SuppressWarnings("deprecation")
	public RequestMappingHandlerMapping requestMappingHandlerMapping(
			@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
			@Qualifier("mvcConversionService") FormattingConversionService conversionService,
			@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {

		RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
		mapping.setOrder(0);
		mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
		mapping.setContentNegotiationManager(contentNegotiationManager);
		mapping.setCorsConfigurations(getCorsConfigurations());

		PathMatchConfigurer pathConfig = getPathMatchConfigurer();
		if (pathConfig.getPatternParser() != null) {
			mapping.setPatternParser(pathConfig.getPatternParser());
		}
		else {
			mapping.setUrlPathHelper(pathConfig.getUrlPathHelperOrDefault());
			mapping.setPathMatcher(pathConfig.getPathMatcherOrDefault());

			Boolean useSuffixPatternMatch = pathConfig.isUseSuffixPatternMatch();
			if (useSuffixPatternMatch != null) {
				mapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
			}
			Boolean useRegisteredSuffixPatternMatch = pathConfig.isUseRegisteredSuffixPatternMatch();
			if (useRegisteredSuffixPatternMatch != null) {
				mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);
			}
		}
		Boolean useTrailingSlashMatch = pathConfig.isUseTrailingSlashMatch();
		if (useTrailingSlashMatch != null) {
			mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
		}
		if (pathConfig.getPathPrefixes() != null) {
			mapping.setPathPrefixes(pathConfig.getPathPrefixes());
		}

		return mapping;
	}

方法映射加载时机

首先看RequestMappingHandlerMapping的diagram图
requestMappingHandlerMapping
从图中可以看出RequestMappingHandlerMapping继承AbstractHandlerMethodMapping,而且AbstractHandlerMethodMapping实现了InitializingBean。InitializingBean的作用:
InitializingBean是Spring提供的拓展性接口,InitializingBean接口为bean提供了属性初始化后的处理方法,它只有一个afterPropertiesSet方法,凡是继承该接口的类,在bean的属性初始化后都会执行该方法,也就是当属性扫描结束后,都会执行该afterPropertiesSet方法,请求映射也是在这里执行的。

在AbstractHandlerMethodMapping类中是这样定义的

	@Override
	public void afterPropertiesSet() {
		initHandlerMethods();
	}
	protected void initHandlerMethods() {
		for (String beanName : getCandidateBeanNames()) {
			//不以SCOPED_TARGET_NAME_PREFIX = "scopedTarget."开始的类执行processCandidateBean(beanName);
			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
				processCandidateBean(beanName);
			}
		}
		handlerMethodsInitialized(getHandlerMethods());
	}
	protected void processCandidateBean(String beanName) {
		//obtainApplicationContext()返回ApplicationContext
		//等价于ApplicationContext.getBean(class)
		//例如返回TestController
		Class<?> beanType = null;
		try {
			beanType = obtainApplicationContext().getType(beanName);
		}
		catch (Throwable ex) {
			// An unresolvable bean type, probably from a lazy bean - let's ignore it.
			if (logger.isTraceEnabled()) {
				logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
			}
		}
		if (beanType != null && isHandler(beanType)) {
			//执行此方法
			detectHandlerMethods(beanName);
		}
	}
protected void detectHandlerMethods(Object handler) {
		//获取正在解析的类 例如 TestController
		Class<?> handlerType = (handler instanceof String ?
				obtainApplicationContext().getType((String) handler) : handler.getClass());

		if (handlerType != null) {
			Class<?> userType = ClassUtils.getUserClass(handlerType);
			Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
					(MethodIntrospector.MetadataLookup<T>) method -> {
						try {
							//返回接口地址映射方法 实际调用为RequestMappingHandlerMapping的getMappingForMethod方法
							//Method作为Key  com.test.TestController.test()
							//T RequestMappingInfo->接口地址以及请求方式 post/get/delete.
							return getMappingForMethod(method, userType);
						}
						catch (Throwable ex) {
							throw new IllegalStateException("Invalid mapping on handler class [" +
									userType.getName() + "]: " + method, ex);
						}
					});
			if (logger.isTraceEnabled()) {
				logger.trace(formatMappings(userType, methods));
			}
			else if (mappingsLogger.isDebugEnabled()) {
				mappingsLogger.debug(formatMappings(userType, methods));
			}
			methods.forEach((method, mapping) -> {
				//方法名 public void com.test.controller.TestController.test()
				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
				//注册方法映射
				registerHandlerMethod(handler, invocableMethod, mapping);
			});
		}
	}

最终注册时调用AbstractHandlerMethodMappin的register方法

public void register(T mapping, Object handler, Method method) {
			this.readWriteLock.writeLock().lock();
			try {
				//省略代码
				//注册方法映射
				//mapping 接口路径 /rest/test |Get
				//handlerMethod com.test.controller.TestController#test()
				//这里的registry就是前文提到的Map<T, MappingRegistration<T>>
				//最终将接口路径保存到内存中
				this.registry.put(mapping,
						new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));
			}
			finally {
				this.readWriteLock.writeLock().unlock();
			}
		}
	@Override
	@Nullable
	protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
		//方法上的RequestMapping值,这里可以是@GetMapping||@PostMapping...
		RequestMappingInfo info = createRequestMappingInfo(method);
		if (info != null) {
			//获取类上的RequestMapping
			RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
			if (typeInfo != null) {
				info = typeInfo.combine(info);
			}
			String prefix = getPathPrefix(handlerType);
			if (prefix != null) {
				info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
			}
		}
		return info;
	}

本文主要记录接口路径扫描到内存中的逻辑,后续持续记录前端调用接口请求,springmvc是如何将请求映射到具体的接口类中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值