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

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.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.kler.cn/news/318577.html

相关文章:

  • notepad++的json查看
  • 8.隐私与安全 - 使用ChatGPT时的注意事项【8/10】
  • 业务安全治理
  • Vue中nextTick的底层原理
  • 【C语言】猜数字游戏
  • LeetCode146 LRU缓存
  • C++解压及压缩(window或linux下编译、使用libarchive)
  • CSS——网格布局(display: grid)之下篇
  • 评论表设计与实现(多级评论)
  • JS的基础语法
  • 在Java中如何利用ClassLoader动态加密、解密Class文件
  • 文本合成语音api接口文档
  • 华为HarmonyOS灵活高效的消息推送服务(Push Kit) -- 10 推送实况窗消息
  • WebGL动画与交互
  • Tableau|二 如何利用功能区创建视图
  • 冒泡排序原理及python代码
  • 需求导向的正则表达式
  • 公安局软件管理平台建设方案和必要性,论文-2-———未来之窗行业应用跨平台架构
  • 2.AFIO 外设:复用和重映射
  • 调试vue build之后的js文件
  • Craft:年度 Mac 应用,卡片式笔记新星
  • 在 Qt 中实现 `QListWidget` 列表项水平居中显示
  • 网关基础知识
  • 线性判别分析(LDA)中求协方差矩阵示例
  • 配置文件--UmiJs
  • 用Flutter几年了,Flutter每个版本有什么区别?
  • 深入理解前端拖拽:从基础实现到事件冒泡与委托的应用【面试真题】
  • MySQL Performance Schema 详解及运行时配置优化
  • mac-m1安装nvm,docker,miniconda
  • 【shell脚本5】Shell脚本学习--条件控制