1.问题的产生

日常开发中,大多数的API层中@Controller注解和@RequestMapping注解都会被使用在其中,但是为什么标注了@Controller和@RequestMapping注解之后,从外部访问你设置的请求路径,springmvc就能捕获到请求并将请求交给指定的类和方法来执行呢?这其实就是RequestMappingHandlerMapping的核心功能了,注册&发现。

2.RequestMappingHandlerMapping之注册

@Controller和@RequestMapping是如何生效的呢,@Controller与其他的@Service,@Component又有什么区别呢,那么就来看下RequestMappingHandlerMapping是如何工作的。

RequestMappingHandlerMapping会在Spring容器初始化时实例化Bean注入到Spring容器中,RequestMappingHandlerMapping继承于RequestMappingInfoHandlerMapping,
RequestMappingInfoHandlerMapping又继承自AbstractHandlerMethodMapping,AbstractHandlerMethodMapping又实现了InitializingBean接口,InitializingBean接口我们都知道,
在Bean的生命周期中,在实例化Bean对象后,开始注入bean依赖的属性,如果实现了BeanNameAware接口,那么紧接着会执行该接口的setBeanName(String name)进行beanName的回调,
之后如果实现了InitializingBean接口则会调用afterPropertiesSet()方法进行一些其他属性的设置。
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
        implements MatchableHandlerMapping, EmbeddedValueResolverAware {}

public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping<RequestMappingInfo> {
}

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {}

public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}

核心就在AbstractHandlerMethodMapping实现的afterPropertiesSet()中,可以看到afterPropertiesSet()中调用了initHandlerMethods()方法

    @Override
    public void afterPropertiesSet() {
        initHandlerMethods();
    }
protected void initHandlerMethods() {
//遍历spring容器中所有beanName
for (String beanName : getCandidateBeanNames()) {
//如果beanName不是以scopedTarget.开头则对该bean进行处理
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
processCandidateBean(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
 
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
//获取该bean的类型
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}

//如果类型!=null&&类型属于handler则进行检测
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}

//RequestMappingHandlerMapping实现的isHandler方法
@Override
protected boolean isHandler(Class<?> beanType) {
//判断该类上是否有@Controller注解或者@RequestMapping注解
   return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}


//检测handler的方法
protected void detectHandlerMethods(Object handler) {
//获取bean类型
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());

if (handlerType != null) {
//获取该类的用户自定义类,该ClassUtils是Spring提供的,通常情况下会返回该类本身的class,如果是cglib代理类则会去返回被代理类本身的class
Class<?> userType = ClassUtils.getUserClass(handlerType);

//将该类的每个方法和对应的url路径(类上@RequestMapping的信息 + 方法上的@RequestMapping的信息进行拼装)封装到map中
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));
}

//遍历这个map,将所有方法和对应的mapping信息进行注册
methods.forEach((method, mapping) -> {
//通过AopUtils处理获取到反射可以调用的方法
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
//将该方法以HandlerMethod的形式进行注册,在SpringMvc的视角下,controller相当于handler,下属方法相当于handlerMethod
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}

 

getMappingForMethod(),通过方法获取详细的Mapping信息(类+方法)

    @Override
    @Nullable
    protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
//先获取方法上@RequestMapping的路径 RequestMappingInfo info
= createRequestMappingInfo(method); if (info != null) {
//获取类上的@RequestMapping的路径 RequestMappingInfo typeInfo
= createRequestMappingInfo(handlerType);
//如果类上存在@RequestMapping则将二者合并,所有路径由类的在前,后面拼接方法上的
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; }


selectInvocableMethod()通过反射获取实际调用的方法
   
public static Method selectInvocableMethod(Method method, @Nullable Class<?> targetType) { if (targetType == null) { return method; } else {
//通过反射获取到方法 Method methodToUse
= MethodIntrospector.selectInvocableMethod(method, targetType);
//判断是否为私有方法/非静态方法/SpringProxy的子类,是则抛出异常,这里有个疑问,反射应该是不能调用静态方法,这里不知道为什么不限制静态方法?
if (Modifier.isPrivate(methodToUse.getModifiers()) && !Modifier.isStatic(methodToUse.getModifiers()) && SpringProxy.class.isAssignableFrom(targetType)) { throw new IllegalStateException(String.format("Need to invoke method '%s' found on proxy for target class '%s' but cannot be delegated to target bean. Switch its visibility to package or protected.", method.getName(), method.getDeclaringClass().getSimpleName())); } else { return methodToUse; } } }

 

将方法以handlerMethod的形式进行注册

    protected void registerHandlerMethod(Object handler, Method method, T mapping) {
        this.mappingRegistry.register(mapping, handler, method);
    }        public void register(T mapping, Object handler, Method method) {

public void register(T mapping, Object handler, Method method) {
//开启写锁,防止多线程注册多个相同的handleMehtod
this.readWriteLock.writeLock().lock();
try {
//构造handlerMethod
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
//校验该handlerMehtod是否可以注册进来
validateMethodMapping(handlerMethod, mapping);
//获取该方法的直接路径
Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
//将该方法的所有直接路径添加mappingRegister中的一个容器pathLookup,主要用来储存直接路径和mapping信息的关系
for (String path : directPaths) {
this.pathLookup.add(path, mapping);
}

String name = null;
if (getNamingStrategy() != null) {
       //获取该方法的名称,如果@RequestMapping上的name有值则取该name,没有则根据类的类型名转换成大写的后+'#'+方法的名称 拼接而成
name = getNamingStrategy().getName(handlerMethod, mapping);
//将该handlerMehod添加进mappingRegister中的一个容器nameLookup,用来存存储name和handleMethod的关系
addMappingName(name, handlerMethod);
}

//跨越相关配置
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
corsConfig.validateAllowCredentials();
this.corsLookup.put(handlerMethod, corsConfig);
}

//将handleMehod注册进容器
this.registry.put(mapping,
new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
 

validateMethodMapping校验handlerMethod是否合法

        private void validateMethodMapping(HandlerMethod handlerMethod, T mapping) {
            // Assert that the supplied mapping is unique.
//根据mapping路径获取HandlerMethod HandlerMethod existingHandlerMethod = this.mappingLookup.get(mapping);
//如果存在并且跟当前的HandlerMethod并且不是同一个则抛错
if (existingHandlerMethod != null && !existingHandlerMethod.equals(handlerMethod)) { throw new IllegalStateException( "Ambiguous mapping. Cannot map '" + handlerMethod.getBean() + "' method \n" + handlerMethod + "\nto " + mapping + ": There is already '" + existingHandlerMethod.getBean() + "' bean method\n" + existingHandlerMethod + " mapped."); } }

 

3.RequestMappingHandlerMapping之发现

通过上面部分的源码我们大概知道了@Controller和@RequestMapping注解是如何生效的,RequestMappingHandlerMapping是如何去处理他们的,那我们说完了注册,就该来说发现,当客户端的请求发送到后台服务器时,SpringMvc是如何根据请求url给他找到指定的方法上呢?我们都知道SpringMVC处理请求的核心在于DisPatcherServlet,也就是所谓的前端控制器,DispatchServlet会拿到请求进行解析分发,那具体是如何操作的,来看下图:

 

 图片来自百度,这张图大家应该都很熟悉了,DispatcherServlet的工作流程,可以看到DispatcherServlet在接收到客户端发送的request时会将请求委托给HandlerMapping去处理,HandlerMapping将根据url寻找一个合适的handler返回给DispatcherServlet,RequestMappingHandlerMapping实现自HandlerMapping接口,他是SpringMVC的众多HandlerMapping中的一个,它们都在Spring容器初始化时一同创建并注入到容器中。而我们的RequestMappingHandlerMapping的发现功能就在此时派上了用场。

 

当请求进来时,dispatcherServlet的doDispatch()方法将对请求处理,部分代码如下:

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        try {
            ModelAndView mv = null;
            Exception dispatchException = null;
            try {
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);

                // Determine handler for the current request.
// 可以看到在此时就要通过handlerMapping去获取HandlerExecutionChain执行链,其中包括了handler和对应的拦截器Inteceptor等等。 mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; }

 

getHandler方法如下:

    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//如果handlerMapping集合不为null,则遍历每个handlerMapping看是否可以找到对应的handler
if (this.handlerMappings != null) { for (HandlerMapping mapping : this.handlerMappings) { HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { return handler; } } } return null; }

 

这里我们主要来看RequestMappingHandlerMapping实现的getHandler()方法,查看源码发现RequestMappingHandlerMapping并没有重写该方法,而是由其祖宗类AbstractHandlerMapping重写了该方法

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//获取handler又是通过getHandlerInternal()方法 Object handler
= getHandlerInternal(request); if (handler == null) { handler = getDefaultHandler(); } if (handler == null) { return null; } // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); } // Ensure presence of cached lookupPath for interceptors and others if (!ServletRequestPathUtils.hasCachedPath(request)) { initLookupPath(request); }
//根据handler和request获取执行链 HandlerExecutionChain executionChain
= getHandlerExecutionChain(handler, request); if (logger.isTraceEnabled()) { logger.trace("Mapped to " + handler); } else if (logger.isDebugEnabled() && !DispatcherType.ASYNC.equals(request.getDispatcherType())) { logger.debug("Mapped to " + executionChain.getHandler()); } if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) { CorsConfiguration config = getCorsConfiguration(handler, request); if (getCorsConfigurationSource() != null) { CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request); config = (globalConfig != null ? globalConfig.combine(config) : config); } if (config != null) { config.validateAllowCredentials(); } executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } //返回执行链 return executionChain; }

 

getHandlerInternal(request)方法又是由AbstractHandlerMapping的子类AbstractHandlerMethodMapping重写的,
    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        String lookupPath = initLookupPath(request);
//对mappingRegister加读锁,防止读取的时候被篡改数据
this.mappingRegistry.acquireReadLock(); try {
//根据request获取handlerMehtod,即找到了实际要执行的方法 HandlerMethod handlerMethod
= lookupHandlerMethod(lookupPath, request); return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally {
//释放读锁
this.mappingRegistry.releaseReadLock(); } }

 

4. 其他的一些补充

在阅读源码中我们也是在不断的在学习,其中的一些方法可能也是第一次见,这里也做解析让自己学习一下

ClassUtils.getUserClass(Class<?> clazz)方法我们在源码中看到有使用来根据calss获取用户自定义类,注意该ClassUtils为Spring包下的

    public static Class<?> getUserClass(Class<?> clazz) {
//判断类名称是否包含cglib的分隔符 CGLIB_CLASS_SEPARATOR = "$$";
if (clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {
//是则取获取当前cglib类的父类,因为cglib代理类是增强类,并不是被代理类本身 Class
<?> superclass = clazz.getSuperclass(); if (superclass != null && superclass != Object.class) { return superclass; } } return clazz; }

 

 可以看到这里cglib代理类的的名称是拼接而成,其中是带着"$$"符号

 

5. 总结

关于RequestMappingHandlerMapping的源码解析暂时先到这了,之后可能会随时更改其中的内容,因为本人目前能力有限,文章中可能存在一些错误的地方,希望大家可以指出一起学习探讨。

 

posted on 2021-12-04 19:30  Yuqi与其  阅读(1212)  评论(0编辑  收藏  举报