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

news2024/11/16 1:40:50

目录

  • 前言
  • 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.parse
		return (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() {
		// 创建 BuilderConfiguration
		this.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() {
		// 获取所有的 BeanNames
		for (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

	@Override
	protected 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) {
			//对类型再次进行处理,主要是针对cglib
			Class<?> 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
	@Nullable
	protected 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源码深度解析》
    如有侵扰,联系删除。 内容仅用于自我记录学习使用。如有错误,欢迎各位大佬指正

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2163240.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring6梳理11——依赖注入之注入List集合类型属性

以上笔记来源&#xff1a; 尚硅谷Spring零基础入门到进阶&#xff0c;一套搞定spring6全套视频教程&#xff08;源码级讲解&#xff09;https://www.bilibili.com/video/BV1kR4y1b7Qc 11 依赖注入之注入List集合类型属性 11.1 创建实体类Emp以及Dept Dept类中添加了遍历Emp…

最大似然估计,存在即合理

一、感性认识 认识的第一步来自感性的认识&#xff0c;先来感性的了解一下最大似然估计。现在&#xff0c;假设有两个学院&#xff0c;物理和外语学院。两个学院都各有特点&#xff0c;物理学院的男生占比大&#xff0c;外语学院女生占比大。如果在一次实验从两个学院中随机的…

记录linux环境下搭建本地MQTT服务器实现mqtt的ssl加密通讯

1、ubuntu安装mosquitto sudo apt-get update//安装服务端 sudo apt-get install mosquitto//安装客户端 sudo apt-get install mosquitto-clients 2、安装openssl 3、mqtts/tls加密传输 mosquitto原生支持了TLS加密&#xff0c;TLS&#xff08;传输层安全&#xff09;是SSL&…

828华为云征文 | 华为云 X 实例服务器存储性能测试与优化策略

目录 引言 1 华为云 X 实例服务器概述 2 存储性能测试方法与工具 2.1 测试方法 2.2 测试工具 3 FIO&#xff08;Flexible I/O Tester&#xff09;读写性能测试 3.1 顺序读写测试 3.2 随机读写测试 4 hdparm性能测试 4.1 实际读取速度测试 4.2 缓存读取速度测试 4.3…

乐鑫ESP-ZeroCode方案设备Matter协议开发新选择,设备无缝连接应用

随着科技的不断进步&#xff0c;智能家居正逐渐成为现代生活的一部分。物联网和智能家居行业应用取得了巨大的增长&#xff0c;一系列无线连接的智能设备涌入家庭&#xff0c;为家庭生活带来自动化和便利。 然而&#xff0c;随着设备数量的增加&#xff0c;用户开始面临一个挑…

LeetCode题练习与总结:删除链表中的节点--237

一、题目描述 有一个单链表的 head&#xff0c;我们想删除它其中的一个节点 node。 给你一个需要删除的节点 node 。你将 无法访问 第一个节点 head。 链表的所有值都是 唯一的&#xff0c;并且保证给定的节点 node 不是链表中的最后一个节点。 删除给定的节点。注意&…

CentOS下安装Kibana(保姆级教程)

前言 Kibana是一个开源的数据分析和可视化平台&#xff0c;通常与Elasticsearch一起使用&#xff0c;用于展示和分析大规模数据集。以下是关于Kibana的一些主要特点和功能&#xff1a; 数据可视化&#xff1a; Kibana允许用户将数据转化为交互式、实时的图形和可视化展示&…

从零开始,Docker进阶之路(三):Docker镜像与命令

一、Docker核心名词 镜像文件、容器、仓库 镜像&#xff1a;简单理解为就是一个安装包&#xff0c;里面包含容器所需要运行的基础文件和配置信息&#xff0c;比如&#xff1a;redis镜像、mysql镜像等。 镜像的来源方式&#xff1a; 1.自己做镜像&#xff0c;比如自己开发微服…

Vue3: readonly与shallowreadonl

目录 一.readonly 1.性质 2.作用 二.shallowReadonly 1.性质 2.作用 三.readonly 四.shallowReadonly 五.运行代码 Vue3中的readonly和shallowReadonly是两个用于创建只读响应式对象的函数。 一.readonly 1.性质 readonly函数会将一个对象或数组包装成一个完全只读…

22 基于51单片机的智能家居系统(DHT11、继电器、气体浓度监测)

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于51单片机&#xff0c;DHT11温湿度检测&#xff0c;CO&#xff0c;PM2.5检测&#xff0c;通过LCD1602显示&#xff0c;超过阈值报警&#xff0c; 继电器驱动风扇转动。通过按键切换选择设置各项…

【linux】gcc makefile

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;linux笔记仓 目录 01.gcc如何完成02.gcc选项03.函数库与动静态链接静态链接动态链接库文件特点和用途动态链接版本和兼容性 04.makefile自动推导 01.gcc如何完成 预处理(进行宏替换) 预处理功能主要…

SAM核心代码注释总结

最近看sam2&#xff0c;顺便注释了下代码&#xff0c;方便回顾和分享。 PS: tensor的维度都基于默认参数配置。 SAM _build_sam sam模块包含三个部分&#xff0c;ImageEncoderViT、PromptEncoder和MaskDecoder&#xff1a; def _build_sam(encoder_embed_dim,encoder_depth…

【开源免费】基于SpringBoot+Vue.JS墙绘产品展示交易平台(JAVA毕业设计)

本文项目编号 T 049 &#xff0c;文末自助获取源码 \color{red}{T049&#xff0c;文末自助获取源码} T049&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…

第Y1周:调用官方权重进行检测

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 一、下载源码 从本周开始YOLO的学习啦。首先是先体验一下。教案选择的是YOLOv5s GITHUB的开源地址如下 github 开源地址 打开网页之后下载zip包解压到自己想…

Windows系统IP地址设置

目录 Windows系统IP地址设置一、背景二、设置步骤1、打开网络连接设置1) 在搜索栏输入“控制面板”&#xff0c;打开控制面板2) 选择“网络和Internet”&#xff0c;进入“网络和共享中心”。3) 进入网络连接设置 2、修改IP地址1) 选中你要修改的网卡&#xff0c;右键点击选择“…

使用Jlink打印单片机的调试信息

1.在工程中添加6个文件 除去RTT_Debug.h外的其他几个文件在jlink安装目录 RTT_Debug.h的内容如下 #ifndef _RTT_H_ #define _RTT_H_#include "SEGGER_RTT.h"#define STR_DEBUG //#define USART_DEBUG#define DBGLOG #define DBGWARNING #define DBGERROR#if def…

深度学习笔记(7)文本标注与NER

深度学习笔记&#xff08;7&#xff09;文本标注与NER 文章目录 深度学习笔记&#xff08;7&#xff09;文本标注与NER一、文本标注1.1文本标注工具doccano1.2 标注处理&#xff0c;bio标注 二、训练模型1.引入库2. 定义数据集3.建模4&#xff0c;模型训练5.评估6.训练 三.测试…

均衡功能,保障安全丨基于极海 G32A1445 汽车通用 MCU 的 BMU 应用方案

BMS电池管理系统是每个电动汽车车企不断优化改进的应用产品&#xff0c;其组成中的BMU用于实现电流检测、绝缘检测、SOC估算、容量累积、报警功能、充放电管理、远程监控等功能。BMU组成包括微控制器系统、充放电管理单元、CAN通信网络单元&#xff08;采集所有从控单体电池信息…

Python近红外光谱数据分析

ChatGPT4.0在近红外光谱数据分析、定性/定量分析模型代码自动生成等方面的强大功能&#xff0c;同时更加系统地学习人工智能&#xff08;包括传统机器学习、深度学习等&#xff09;的基础理论&#xff0c;以及具体的代码实现方法掌握ChatGPT4.0在科研工作中的各种使用方法与技巧…

YOLOv10改进 | 特征融合篇,YOLOv10添加iAFF(多尺度通道注意力模块),二次创新C2f结构,提升小目标检测能力

摘要 特征融合,即来自不同层或分支的特征的组合,是现代网络架构中无处不在的一部分。虽然它通常通过简单的操作(如求和或拼接)来实现,但这种方式可能并不是最佳选择。在这项工作中,提出了一种统一且通用的方案,即注意力特征融合(Attentional Feature Fusion),适用于…