SpringMVC之RequestMappingHandlerMapping
Posted 木叶之荣
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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);
以上是关于SpringMVC之RequestMappingHandlerMapping的主要内容,如果未能解决你的问题,请参考以下文章
springmvc学习笔记-springmvc整合mybatis之controller
springmvc学习笔记-springmvc整合mybatis之service
springmvc学习笔记(11)-springmvc注解开发之简单参数绑定