当前位置: 首页 > news >正文

Spring源码学习:SpringMVC(3)mvcannotation-driven标签解析【RequestMappingHandlerMapping生成】

目录

  • 前言
  • mvc:annotation-driven标签概述
  • mvc:annotation-driven标签解析【RequestMappingHandlerMapping生成】
    • AnnotationDrivenBeanDefinitionParser#parse (解析入口)
    • RequestMappingHandlerMapping的实例化
      • 类图
      • afterPropertiesSet
      • AbstractHandlerMethodMapping#afterPropertiesSet
      • processCandidateBean
      • isHandler
      • detectHandlerMethods
      • MethodIntrospector#selectMethods
      • ReflectionUtils#doWithMethods
      • getMappingForMethod
      • createRequestMappingInfo
      • registerHandlerMethod
    • 总结

前言

​ 上一篇我们已经完成了springmvc中子容器的初始化,子容器里面一般是一些和web相关的组件,其中的配置文件中就有mvc:annotation-driven这个标签,这里主要对这个标签来进行剖析,看下它里面干了一些什么,其实也是为了后面我们能通过请求url找到对应处理器方法完成的一个铺垫

mvc:annotation-driven标签概述

​ mvc:annotation-driven标签默认会开启SpringMVC的注解驱动模式,默认注册一个RequestMappingHandlerMapping、一个RequestMappingHandlerAdapter、一个ExceptionHandlerExceptionResolver。以支持对使用了 @RequestMapping 、 @ExceptionHandler 及其他注解的控制器方法的请求处理。

mvc:annotation-driven标签解析【RequestMappingHandlerMapping生成】

​ 首先这是一个XML标签,所以我们需要到Spring中refresh()核心方法中的obtainFreshBeanFactory()中里面的parseBeanDefinitions(root,delegate)方法那里打断点,找到解析 mvc:annotation-driven这个标签的逻辑

在这里插入图片描述

注意:node一定得是 mvc:annotation-driven这个标签

	public BeanDefinition parseCustomElement(Element ele) {return parseCustomElement(ele, null);}public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {//解析节点的命名空间String namespaceUri = getNamespaceURI(ele);if (namespaceUri == null) {return null;}//解析命名空间,得到一个命名空间处理器//重点NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);if (handler == null) {error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);return null;}//开始解析//主线 重点return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));\}public BeanDefinition parse(Element element, ParserContext parserContext) {//通过定义的标签属性(如:component-scan)获取对应的BeanDefinitionParser解析对象BeanDefinitionParser parser = findParserForElement(element, parserContext);//执行解析   AnnotationDrivenBeanDefinitionParser.parsereturn (parser != null ? parser.parse(element, parserContext) : null);}

​ 关于定位自定义标签解析的过程,以后的IOC中会说明的,这里直接打开AnnotationDrivenBeanDefinitionParser类并定位到其parse方法

AnnotationDrivenBeanDefinitionParser#parse (解析入口)

/*** 解析 mvc:annotation-driven 标签*/
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {Object source = parserContext.extractSource(element);XmlReaderContext readerContext = parserContext.getReaderContext();CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);parserContext.pushContainingComponent(compDefinition);/*** 获取协商内容视图配置*/RuntimeBeanReference contentNegotiationManager = getContentNegotiationManager(element, source, parserContext);/*** 创建RequestMappingHandlerMapping的RootBeanDefinition* 从这里也可以看出,开启mvc:annotation-driven标签后,* 将会默认注册RequestMappingHandlerMapping作为默认的HandlerMapping*/RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);handlerMappingDef.setSource(source);handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);handlerMappingDef.getPropertyValues().add("order", 0);handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);// 是否开启矩阵变量if (element.hasAttribute("enable-matrix-variables")) {Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enable-matrix-variables"));handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);}// 解析path-matching路径匹配标签configurePathMatchingProperties(handlerMappingDef, element, parserContext);readerContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME , handlerMappingDef);// 解析cors跨域标签RuntimeBeanReference corsRef = MvcNamespaceUtils.registerCorsConfigurations(null, parserContext, source);handlerMappingDef.getPropertyValues().add("corsConfigurations", corsRef);// 解析conversion-service数据转换、格式化标签RuntimeBeanReference conversionService = getConversionService(element, source, parserContext);// 解析validator标签RuntimeBeanReference validator = getValidator(element, source, parserContext);// 解析message-codes-resolver标签RuntimeBeanReference messageCodesResolver = getMessageCodesResolver(element);/*** 创建ConfigurableWebBindingInitializer的RootBeanDefinition对象* 并将上一步解析的conversionService、validator、messageCodesResolver* 作为属性注入到该对象中*/RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);bindingDef.setSource(source);bindingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);bindingDef.getPropertyValues().add("conversionService", conversionService);bindingDef.getPropertyValues().add("validator", validator);bindingDef.getPropertyValues().add("messageCodesResolver", messageCodesResolver);// 解析message-converters标签ManagedList<?> messageConverters = getMessageConverters(element, source, parserContext);// 解析argument-resolvers标签ManagedList<?> argumentResolvers = getArgumentResolvers(element, parserContext);// 解析return-value-handlers标签ManagedList<?> returnValueHandlers = getReturnValueHandlers(element, parserContext);// 解析async-support标签String asyncTimeout = getAsyncTimeout(element);// 解析async-support的task-executor子标签RuntimeBeanReference asyncExecutor = getAsyncExecutor(element);// 解析async-support的callable-interceptors子标签ManagedList<?> callableInterceptors = getCallableInterceptors(element, source, parserContext);// 解析async-support的deferred-result-interceptors子标签ManagedList<?> deferredResultInterceptors = getDeferredResultInterceptors(element, source, parserContext);/*** 创建RequestMappingHandlerAdapter的RootBeanDefinition* 从这里也可以看出,开启mvc:annotation-driven标签后,* 将会默认注册RequestMappingHandlerAdapter作为默认的HandlerAdapter* 并将上面解析的内容绑定到该HandlerAdapter中*/RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);handlerAdapterDef.setSource(source);handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);handlerAdapterDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);handlerAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);handlerAdapterDef.getPropertyValues().add("messageConverters", messageConverters);addRequestBodyAdvice(handlerAdapterDef);addResponseBodyAdvice(handlerAdapterDef);if (element.hasAttribute("ignore-default-model-on-redirect")) {Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute("ignore-default-model-on-redirect"));handlerAdapterDef.getPropertyValues().add("ignoreDefaultModelOnRedirect", ignoreDefaultModel);}if (argumentResolvers != null) {handlerAdapterDef.getPropertyValues().add("customArgumentResolvers", argumentResolvers);}if (returnValueHandlers != null) {handlerAdapterDef.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);}if (asyncTimeout != null) {handlerAdapterDef.getPropertyValues().add("asyncRequestTimeout", asyncTimeout);}if (asyncExecutor != null) {handlerAdapterDef.getPropertyValues().add("taskExecutor", asyncExecutor);}handlerAdapterDef.getPropertyValues().add("callableInterceptors", callableInterceptors);handlerAdapterDef.getPropertyValues().add("deferredResultInterceptors", deferredResultInterceptors);readerContext.getRegistry().registerBeanDefinition(HANDLER_ADAPTER_BEAN_NAME , handlerAdapterDef);/*** 创建CompositeUriComponentsContributorFactoryBean的RootBeanDefinition* CompositeUriComponentsContributorFactoryBean是一个工厂bean,* 可以用来获取RequestMappingHandlerAdapter中的HandlerMethodArgumentResolver配置*/RootBeanDefinition uriContributorDef = new RootBeanDefinition(CompositeUriComponentsContributorFactoryBean.class);uriContributorDef.setSource(source);uriContributorDef.getPropertyValues().addPropertyValue("handlerAdapter", handlerAdapterDef);uriContributorDef.getPropertyValues().addPropertyValue("conversionService", conversionService);String uriContributorName = MvcUriComponentsBuilder.MVC_URI_COMPONENTS_CONTRIBUTOR_BEAN_NAME;readerContext.getRegistry().registerBeanDefinition(uriContributorName, uriContributorDef);/*** 创建ConversionServiceExposingInterceptor的RootBeanDefinition* 主要用来解析spring:eval标签*/RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);csInterceptorDef.setSource(source);csInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, conversionService);RootBeanDefinition mappedInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);mappedInterceptorDef.setSource(source);mappedInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, (Object) null);mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, csInterceptorDef);String mappedInterceptorName = readerContext.registerWithGeneratedName(mappedInterceptorDef);/*** 创建ExceptionHandlerExceptionResolver的RootBeanDefinition*/RootBeanDefinition methodExceptionResolver = new RootBeanDefinition(ExceptionHandlerExceptionResolver.class);methodExceptionResolver.setSource(source);methodExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);methodExceptionResolver.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);methodExceptionResolver.getPropertyValues().add("messageConverters", messageConverters);methodExceptionResolver.getPropertyValues().add("order", 0);addResponseBodyAdvice(methodExceptionResolver);if (argumentResolvers != null) {methodExceptionResolver.getPropertyValues().add("customArgumentResolvers", argumentResolvers);}if (returnValueHandlers != null) {methodExceptionResolver.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);}String methodExResolverName = readerContext.registerWithGeneratedName(methodExceptionResolver);/*** 创建ResponseStatusExceptionResolver的RootBeanDefinition**/RootBeanDefinition statusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class);statusExceptionResolver.setSource(source);statusExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);statusExceptionResolver.getPropertyValues().add("order", 1);String statusExResolverName = readerContext.registerWithGeneratedName(statusExceptionResolver);/*** 创建DefaultHandlerExceptionResolver的RootBeanDefinition* 该类是HandlerExceptionResolver的默认实现,可以解析http异常并将相应的http状态码返回* 例如:404*/RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class);defaultExceptionResolver.setSource(source);defaultExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);defaultExceptionResolver.getPropertyValues().add("order", 2);String defaultExResolverName = readerContext.registerWithGeneratedName(defaultExceptionResolver);/*** 将上面创建的RootBeanDefinition以组件形式纳入SpringIOC容器*/parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, HANDLER_MAPPING_BEAN_NAME));parserContext.registerComponent(new BeanComponentDefinition(handlerAdapterDef, HANDLER_ADAPTER_BEAN_NAME));parserContext.registerComponent(new BeanComponentDefinition(uriContributorDef, uriContributorName));parserContext.registerComponent(new BeanComponentDefinition(mappedInterceptorDef, mappedInterceptorName));parserContext.registerComponent(new BeanComponentDefinition(methodExceptionResolver, methodExResolverName));parserContext.registerComponent(new BeanComponentDefinition(statusExceptionResolver, statusExResolverName));parserContext.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExResolverName));// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"// 注册默认组件MvcNamespaceUtils.registerDefaultComponents(parserContext, source);parserContext.popAndRegisterContainingComponent();return null;
}

那么接下来我们需要总结一下,如果mvc:annotation-driven没有配置任何子标签的话,Spring会如何处理呢?

RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
RootBeanDefinition uriContributorDef = new RootBeanDefinition(CompositeUriComponentsContributorFactoryBean.class);
RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);
RootBeanDefinition mappedInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);
RootBeanDefinition methodExceptionResolver = new RootBeanDefinition(ExceptionHandlerExceptionResolver.class);
RootBeanDefinition statusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class);
RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class);

可以看到即使不做任何子标签的配置,SpringMVC默认也会创建上述9个内部bean的实例。

RequestMappingHandlerMapping的实例化

类图

在这里插入图片描述

​ 上图信息比较多,我们查找关键信息。可以看到这个类间接实现了HandlerMapping接口,是HandlerMapping类型的实例。

除此之外还实现了ApplicationContextAwareIntitalzingBean 这两个接口。在这里简要介绍一下这两个接口:

如果一个类实现了ApplicationContextAware接口,Spring容器在初始化该类时候会自动回调该类的setApplicationContext()方法。这个接口主要用来让实现类得到Spring 容器上下文信息。

如果一个bean实现了IntitalzingBean接口,Spring 容器初始化bean时会回调afterPropertiesSet()方法。这个接口的主要作用是让bean在初始化时可以实现一些自定义的操作。

RequestMappingHandlerMapping实现了InitializingBean接口,当设置完属性后肯定会回调afterPropertiesSet方法,再看afterPropertiesSet方法逻辑。

afterPropertiesSet

	public void afterPropertiesSet() {// 创建 BuilderConfigurationthis.config = new RequestMappingInfo.BuilderConfiguration();this.config.setUrlPathHelper(getUrlPathHelper());this.config.setPathMatcher(getPathMatcher());this.config.setSuffixPatternMatch(useSuffixPatternMatch());this.config.setTrailingSlashMatch(useTrailingSlashMatch());this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());this.config.setContentNegotiationManager(getContentNegotiationManager());super.afterPropertiesSet();}

上面调用了父类AbstractHandlerMethodMapping的afterPropertiesSet()方法,沿调用栈继续查看。

AbstractHandlerMethodMapping#afterPropertiesSet

	public void afterPropertiesSet() {initHandlerMethods();}protected void initHandlerMethods() {// 获取所有的 BeanNamesfor (String beanName : getCandidateBeanNames()) {// 判断不是已 scopedTarget 开头if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {//对含有注解的bean进行处理,获取handler函数信息。processCandidateBean(beanName);}}//简单的日志记录handlerMethodsInitialized(getHandlerMethods());}

processCandidateBean是核心方法,该方法内部完成了bean的筛选和对某个Controller内部所有handlerMethod的探测。

processCandidateBean

protected void processCandidateBean(String beanName) {Class<?> beanType = null;try {// 获取具体的类型.beanType = obtainApplicationContext().getType(beanName);}catch (Throwable ex) {// An unresolvable bean type, probably from a lazy bean - let's ignore it.// 一个无法解析的bean类型,可能来自一个lazy bean-让我们忽略它。// 日志打印...if (logger.isTraceEnabled()) {logger.trace("Could not resolve type for bean '" + beanName + "'", ex);}}// 不是null 并且 类型是存在 @Controller 或者 @RequestMapping  注解if (beanType != null && isHandler(beanType)) {//如果当前bean是一个handler,那么需要探测出该handler内部所有handlerMethod实现detectHandlerMethods(beanName);}}

如果当前bean是一个handler的话则进入detectHandlerMethods(beanName);

isHandler

	@Overrideprotected boolean isHandler(Class<?> beanType) {// 存在 Controller注解 或者存在 RequestMapping 注解 ..return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));}

上面逻辑很简单,就是判断该bean是否有@Controller@RequestMapping注解,然后返回判断结果。

如果含有这两个注解之一就进入detectHandlerMethods()方法进行处理。

detectHandlerMethods

protected void detectHandlerMethods(Object handler) {// 如果传递是 String 则 获取其类型 ,如果是是class 则直接返回Class<?> handlerType = (handler instanceof String ?obtainApplicationContext().getType((String) handler) : handler.getClass());if (handlerType != null) {//对类型再次进行处理,主要是针对cglibClass<?> userType = ClassUtils.getUserClass(handlerType);//遍历方法,对注解中的信息进行处理,得到RequestMappingInfo对象,得到methods数组Map<Method, T> methods = MethodIntrospector.selectMethods(userType,(MethodIntrospector.MetadataLookup<T>) method -> {try {// 里面主要是  获取 方法 和类上的 @RequestMapping 将其合并.// 如果没有的话则会返回 null,而由于是 lambda 这里主要是制订过滤规则// 如果返回了 null 则 selectMethods 不会将其放入到Map中。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));}//遍历methods[Method,{path}]methods.forEach((method, mapping) -> {//对方法的可访问性进行校验,如private,static,SpringProxy, //获取最终请求路径Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);// 注册,注册到全局的MappingRegistry实例里registerHandlerMethod(handler, invocableMethod, mapping);});}}

​ 上面方法中用了几个回调,可能看起来比较复杂,我们先看MethodIntrospector.selectMethods,该方法最终会经过筛选完成一个method-》RequestMappingInfo(@RequestMapping注解信息)的映射

MethodIntrospector#selectMethods

public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {final Map<Method, T> methodMap = new LinkedHashMap<>();Set<Class<?>> handlerTypes = new LinkedHashSet<>();Class<?> specificHandlerType = null;//把自身类添加到handlerTypes中if (!Proxy.isProxyClass(targetType)) {specificHandlerType = ClassUtils.getUserClass(targetType);handlerTypes.add(specificHandlerType);}//获取该bean所有的接口,并添加到handlerTypes中handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));//对自己及所有实现接口进行遍历for (Class<?> currentHandlerType : handlerTypes) {final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);//获取函数映射信息ReflectionUtils.doWithMethods(currentHandlerType, method -> {Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);// //回调inspect()方法,这里就会调用到外面的getMappingForMethod函数来生成RequestMappingInfo  T result = metadataLookup.inspect(specificMethod);if (result != null) {Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {methodMap.put(specificMethod, result);}}}, ReflectionUtils.USER_DECLARED_METHODS);}return methodMap;}

MethodIntrospector.selectMethods的作用可以简单看成是遍历了handler类内部的所有方法,包括其父类和实现接口里面的所有方法,然后交给注册进来的回调接口进行处理,回调接口的返回值作为生成的映射信息,如果返回值不为空,就和当前method组成一条记录,放入map中; 遍历完所有方法后,返回该map集合。

selectMethods完成方法筛选的关键就在于目标方法经过回调接口处理过后,返回值是否为空,如果为空,说明当前方法需要被过滤掉

我们看到又调用了ReflectionUtils.doWithMethods()

ReflectionUtils#doWithMethods

public static void doWithMethods(Class<?> clazz, MethodCallback mc, @Nullable MethodFilter mf) {// Keep backing up the inheritance hierarchy.Method[] methods = getDeclaredMethods(clazz, false);for (Method method : methods) {if (mf != null && !mf.matches(method)) {continue;}try {//这里调用的doWith方法就会回到调用方法那里的lambda方法里面mc.doWith(method);}catch (IllegalAccessException ex) {throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex);}}if (clazz.getSuperclass() != null && (mf != USER_DECLARED_METHODS || clazz.getSuperclass() != Object.class)) {doWithMethods(clazz.getSuperclass(), mc, mf);}else if (clazz.isInterface()) {for (Class<?> superIfc : clazz.getInterfaces()) {doWithMethods(superIfc, mc, mf);}}}

doWith中执行metadataLookup.inspect方法,也就是会去执行getMappingForMethod。该方法会根据method来构建RequestMappingInfo,该对象记录了匹配这个method的所有需要满足的条件

可以简单将上面理解为遍历当前handler类及其实现接口,并获取其中所有的方法,最后进入getMappingForMethod方法中

getMappingForMethod

	@Override@Nullableprotected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {RequestMappingInfo info = createRequestMappingInfo(method);if (info != null) {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;}

​ 进入createRequestMappingInfo(method),内部会判断是否存在RequestMapping注解,如果存在才创建,可以看到先根据方法创建一个,之后再根据类创建一个,最后会合并一下,因为需要匹配的上请求url是需要类上的path+方法上的path一起来完成

createRequestMappingInfo

	private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);RequestCondition<?> condition = (element instanceof Class ?getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);}protected RequestMappingInfo createRequestMappingInfo(RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {RequestMappingInfo.Builder builder = RequestMappingInfo.paths(resolveEmbeddedValuesInPatterns(requestMapping.path())).methods(requestMapping.method()).params(requestMapping.params()).headers(requestMapping.headers()).consumes(requestMapping.consumes()).produces(requestMapping.produces()).mappingName(requestMapping.name());if (customCondition != null) {builder.customCondition(customCondition);}return builder.options(this.config).build();}

上面把RequestMapping注解中的信息都放到一个RequestMappingInfo实例中后返回。之后返回的话最终会到selectMethods方法里面,判断metadataLookup.inspect的返回值是否为null,不为null则将方法和RequestMappingInfo的信息维护到map中,下面就是注册RequestMappingInfo

registerHandlerMethod

protected void registerHandlerMethod(Object handler, Method method, T mapping) {this.mappingRegistry.register(mapping, handler, method);}public void register(T mapping, Object handler, Method method) {// Assert that the handler method is not a suspending one.if (KotlinDetector.isKotlinType(method.getDeclaringClass())) {Class<?>[] parameterTypes = method.getParameterTypes();if ((parameterTypes.length > 0) && "kotlin.coroutines.Continuation".equals(parameterTypes[parameterTypes.length - 1].getName())) {throw new IllegalStateException("Unsupported suspending handler method detected: " + method);}}this.readWriteLock.writeLock().lock();try {//处理方法的对象HandlerMethod handlerMethod = createHandlerMethod(handler, method);//判断映射的唯一性validateMethodMapping(handlerMethod, mapping);//将mapping信息和控制器方法对应this.mappingLookup.put(mapping, handlerMethod);//将path与处理器映射(一个方法可能可以处理多个url)List<String> directUrls = getDirectUrls(mapping);for (String url : directUrls) {this.urlLookup.add(url, mapping);}//控制器名的大写英文缩写#方法名String name = null;if (getNamingStrategy() != null) {name = getNamingStrategy().getName(handlerMethod, mapping);addMappingName(name, handlerMethod);}//跨域请求相关配置CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);if (corsConfig != null) {this.corsLookup.put(handlerMethod, corsConfig);}//将所有配置统一注册到registry中this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));}finally {this.readWriteLock.writeLock().unlock();}}

​ 可以看到将Mehtod和Handler给封装了一层,变成了HandlerMethod,然后最终维护了3个map(不包括跨域)

  • this.mappingLookup : 保存requestMappingInfo和控制器方法的映射
  • this.urlLookup :保存url和多个控制器方法的映射(主要是因为restFul风格)
  • this.registry : 保存requestMappingInfo和registration的映射
	class MappingRegistry {//保存RequestMappingInfo和MappingRegistration的映射关系private final Map<T, MappingRegistration<T>> registry = new HashMap<>();//保存RequestMappingInfo和HandlerMethod的映射关系private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();//保存请求路径和RequestMappingInfo的映射关系private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();//保存handlerMethodName和handlerMethod的映射关系  private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();//保存handlerMethod和跨域配置的映射关系private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();//读写锁 private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();...

总结

  1. mvc:annotation-driven标签在子容器初始化的过程中会完成扫描,会注入一些bean,其中就包括RequestMappingHandlerMapping
  2. RequestMappingHandlerMapping实现了InitializingBean接口,所有会在其他bean执行生命周期的时候调用其afterPropertiesSet方法
  3. 然后判断当前bean是否为一个controller,主要针对类上是否存在@Controller 或者 @RequestMapping注解
  4. 之后会通过遍历当前类以及其实现的接口类型,获取其中的方法,判断方法是否存在@RequestMapping注解,如果存在则创建RequestMappingInfo实例,该实例中存放了@RequestMappingInfo注解信息(需要注意,创建@RequestMappingInfo会进行两次,一次是根据方法的,一次是根据类,之后会合并为一个,主要为了path的完整),最后会将RequestMappingInfo和相对应的method保存到map中
  5. 通过上一步的map来完成注册,将信息保存到其父类AbstractHandlerMethodMapping类内部MappingRegistry中的三个map中

    以上:内容部分参考:《Spring源码深度解析》
    如有侵扰,联系删除。 内容仅用于自我记录学习使用。如有错误,欢迎各位大佬指正

相关文章:

  • 技术成神之路:设计模式(十六)代理模式
  • Python库matplotlib之五
  • 【RabbitMq源码阅读】分析RabbitMq发送消息源码
  • Robot Operating System——一组三维空间中的位姿(位置和方向)
  • Flink集群部署
  • kafka下载配置
  • Go 1.19.4 序列化和反序列化-Day 16
  • 速盾:视频开cdn合适还是视频点播合适?
  • 大模型智能体在金融公告理解领域的应用 | OPENAIGC开发者大赛高校组AI创新之星奖
  • 语音音频(wav)声纹识别-技术实现-python
  • 【JavaEE初阶】网络原理
  • 性能优化与资源管理:优化Selenium脚本的执行效率,合理管理浏览器实例和系统资源
  • CSS给一行按钮统一设置间隔
  • DarkLabel2.4版本导入MOT17数据集
  • 如何解决跨境电商税务管理难题
  • 4月23日世界读书日 网络营销论坛推荐《正在爆发的营销革命》
  • Android开源项目规范总结
  • Git初体验
  • Javascripit类型转换比较那点事儿,双等号(==)
  • JavaScript DOM 10 - 滚动
  • Linux gpio口使用方法
  • magento 货币换算
  • Mybatis初体验
  • Webpack4 学习笔记 - 01:webpack的安装和简单配置
  • 漂亮刷新控件-iOS
  • 深度学习在携程攻略社区的应用
  • 使用Swoole加速Laravel(正式环境中)
  • 腾讯大梁:DevOps最后一棒,有效构建海量运营的持续反馈能力
  • 我感觉这是史上最牛的防sql注入方法类
  • 回归生活:清理微信公众号
  • 昨天1024程序员节,我故意写了个死循环~
  • ​​​​​​​GitLab 之 GitLab-Runner 安装,配置与问题汇总
  • #define MODIFY_REG(REG, CLEARMASK, SETMASK)
  • $refs 、$nextTic、动态组件、name的使用
  • (+4)2.2UML建模图
  • (ISPRS,2023)深度语义-视觉对齐用于zero-shot遥感图像场景分类
  • (苍穹外卖)day03菜品管理
  • (二)十分简易快速 自己训练样本 opencv级联lbp分类器 车牌识别
  • (附源码)c#+winform实现远程开机(广域网可用)
  • (附源码)计算机毕业设计SSM基于java的云顶博客系统
  • (学习日记)2024.01.19
  • (转)平衡树
  • (自用)gtest单元测试
  • .[hudsonL@cock.li].mkp勒索病毒数据怎么处理|数据解密恢复
  • .net项目IIS、VS 附加进程调试
  • @Builder用法
  • [ Algorithm ] N次方算法 N Square 动态规划解决
  • [ 渗透工具篇 ] 一篇文章让你掌握神奇的shuize -- 信息收集自动化工具
  • [2019/05/17]解决springboot测试List接口时JSON传参异常
  • [android] 切换界面的通用处理
  • [Android]如何调试Native memory crash issue
  • [AutoSar]BSW_Memory_Stack_004 创建一个简单NV block并调试
  • [Bug]使用gradio创建应用提示AttributeError: module ‘gradio‘ has no attribute ‘inputs‘
  • [C#]winform部署官方yolov10目标检测的onnx模型
  • [c++] 什么是平凡类型,标准布局类型,POD类型,聚合体