SpringMVC之RequestMappingHandlerMapping(二)

我们在上篇文章中大致说了一些和RequestMappingHandlerMapping相关的类,我们在这篇文章中重点分析下RequestMappingHandlerMapping这个类。从上篇的文章中我们看到,RequestMappingHandlerMapping这个类实现了和Bean的生命周期相关的一些接口(关于Bean的生命周期可以参考之前写的小文章:Spring Bean的生命周期小析(一)Spring Bean的生命周期小析(二)),Bean的生命周期的这些接口为我们在Bean的实力化的过程中对Bean进行扩展提供了很大的帮助。在RequestMappingHandlerMapping中我们先看它对InitializingBean这个接口的实现:

	@Override
	public void afterPropertiesSet() {
		//创建一个BuilderConfiguration的实例,这个实例是用来创建RequestMappingInfo使用
		this.config = new RequestMappingInfo.BuilderConfiguration();
		//下面这些是设置了和请求映射相关的一些值
		this.config.setUrlPathHelper(getUrlPathHelper());
		this.config.setPathMatcher(getPathMatcher());
		//是否使用后缀模式匹配
		this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
		//是否使用末尾/匹配
		this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
		//设置后缀模式匹配是否仅现在文件扩展使用
		this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
		//主要是给ProducesRequestCondition使用的
		this.config.setContentNegotiationManager(getContentNegotiationManager());
		//调用父类的afterPropertiesSet方法进行一些设置
		super.afterPropertiesSet();
	}

在上面的afterPropertiesSet方法中,为BuilderConfiguration设置了一些变量,那么这些变量的值是从哪儿来的呢?因为我们的项目是用SpringBoot的,所以我们这里只分析SpringBoot中对RequestMappingHandlerMapping的加载过程。我这里先直接给出答案,后面我们在详细的分析SpringBoot对Spring MVC的自动加载。变量的初值是在这个方法礼设置的org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#requestMappingHandlerMapping。我们先不分析。继续分析super.afterPropertiesSet()这个方法。这个方法在AbstractHandlerMethodMapping中。

	@Override
	public void afterPropertiesSet() {
		initHandlerMethods();
	}
	protected void initHandlerMethods() {
		//获取所有的Bean名称
		for (String beanName : getCandidateBeanNames()) {
			//beanName不是以scopedTarget.开头的Bean。这个一般是在AOP中才会出现这样的Bean
			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
				//处理Bean 重点要分析的方法
				processCandidateBean(beanName);
			}
		}
		handlerMethodsInitialized(getHandlerMethods());
	}

根据beanName判断类上是否带有Controller或者RequestMapping注解,如果有的话,则解析其中的带有RequestMapping注解的方法,并注册到MappingRegistry

	protected void processCandidateBean(String beanName) {
		Class<?> beanType = null;
		try {
			//从ApplicationContext中根据beanName获取Bean的类型
			beanType = obtainApplicationContext().getType(beanName);
		}
		catch (Throwable ex) {
			....
		}
		//判断这个Bean是不是带有Controller或者RequestMapping注解(包含父类)
		if (beanType != null && isHandler(beanType)) {
			//检测Bean中带有RequestMapping的Method封装成HandlerMethod
			//这个方法极其复杂
			detectHandlerMethods(beanName);
		}
	}

解析Bean中的带有RequestMapping注解的方法,注册到MappingRegistry中

	protected void detectHandlerMethods(Object handler) {
		//这里同样是先获取Bean的Class
		Class<?> handlerType = (handler instanceof String ?
				obtainApplicationContext().getType((String) handler) : handler.getClass());

		if (handlerType != null) {
			//这里是获取真正的Class,因为我们的类可能被CGlib代理类,如果被CGlib动态代理了的话,就获取它的父类
			Class<?> userType = ClassUtils.getUserClass(handlerType);
			//这里是获取Class中相应的方法,这里写的很复杂,并且用了JDK1.8的一些新特性
			//我们在下面慢慢分析
			Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
					(MethodIntrospector.MetadataLookup<T>) method -> {
						try {
							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));
			}
			//循环上面获取到的Method,进行一系列的封装
			methods.forEach((method, mapping) -> {
				//获取真正的可执行的方法
				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
				//向MappingRegistry中注册Method
				registerHandlerMethod(handler, invocableMethod, mapping);
			});
		}
	}

MethodIntrospector.selectMethods这个方法是一个通用的方法,唯一的不同是MetadataLookup这个参数实现,我们这里的实现是用类lambda表达式,主要的是会调用getMappingForMethod这个方法

	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;
		//如果不是JDK动态代理生成的类
		if (!Proxy.isProxyClass(targetType)) {
			//如果是CGlib动态代理生成的类,则获取它的父类
			specificHandlerType = ClassUtils.getUserClass(targetType);
			handlerTypes.add(specificHandlerType);
		}
		//获取Class上的所有接口,如果传入的Class是一个接口的话,则直接返回这个接口
		handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));
		//循环处理上一步获取到的Class
		for (Class<?> currentHandlerType : handlerTypes) {
			//这里如果传入的Class是JDK动态代理生成的类,则targetClass和currentHandlerType是一样的,每次都会变
			//如果传入的Class不是JDK动态代理生成的类,则targetClass和currentHandlerType是不一样的。targetClass不会变
			final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);
			//这个地方就是我们分析的重点 这里也同样用到类JDK1.8的新特性
			ReflectionUtils.doWithMethods(currentHandlerType, method -> {
				Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
				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;
	}

我们来看下org.springframework.util.ReflectionUtils#doWithMethods这个方法的实现,这个方法也是一个通用的方法,不同的是MethodCallback和MethodFilter接口的实现。我们这里的MethodCallback的实现是通过lambda表达是实现的,而MethodFilter的实现是

//要这个方法不是桥接方法同时也不是isSynthetic方法同时也不是Object中的方法
//关于isSynthetic 可以参考这里: https://www.cnblogs.com/bethunebtj/p/7761596.html
//关于桥接方法可以参考这里: https://stackoverflow.com/questions/289731/what-method-isbridge-used-for https://www.zhihu.com/question/54895701
public static final MethodFilter USER_DECLARED_METHODS =
			(method -> !method.isBridge() && !method.isSynthetic() && method.getDeclaringClass() != Object.class);

接着看doWithMethods这个方法的实现

	public static void doWithMethods(Class<?> clazz, MethodCallback mc, @Nullable MethodFilter mf) {
		// Keep backing up the inheritance hierarchy.
		//获取所有的方法,包含实现的接口中的default方法
		Method[] methods = getDeclaredMethods(clazz);
		for (Method method : methods) {
			//这个mf就是上面说的USER_DECLARED_METHODS 
			//mf.matches 这个方法就是判断这个方法:
			//不是桥接方法同时也不是isSynthetic方法同时也不是Object中的方法
			//如果是桥接方法,则返回false,桥接方法不做处理
			if (mf != null && !mf.matches(method)) {
				continue;
			}
			try {
				//调用mc的doWith方法进行处理
				mc.doWith(method);
			}
			catch (IllegalAccessException ex) {
				throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex);
			}
		}
		//递归调用父Class进行处理
		if (clazz.getSuperclass() != null) {
			doWithMethods(clazz.getSuperclass(), mc, mf);
		}
		else if (clazz.isInterface()) {
			for (Class<?> superIfc : clazz.getInterfaces()) {
				doWithMethods(superIfc, mc, mf);
			}
		}
	}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值