Spring Web MVC实现原理

111 阅读2分钟

前言

本文主要分析Spring Web MVC中一次请求的处理过程和Controller路径映射解析过程。

DispatcherServlet

DispatcherServlet是Spring Web MVC的核心组件,本质上DispatcherServlet是一个Servlet。继承图如下: DispatcherServlet委托了很多特殊的bean来完成对请求的处理和渲染响应,这些bean都是被Spring管理的对象。

DispatherServlet组列表

DispatcherServlet委托了很多特殊的bean来完成对请求的处理和渲染响应,这些bean都是被Spring管理的对象。

Bean TypeExplanation
HandlerMappinghandler和request的映射关系,HandlerMapping有两个主要的实现类RequestMappingHandlerMapping(对@RequestMapping注解的支持)和SimpleUrlHandlerMapping 显示的注册URI和handlers
HandlerAdapter帮助DispatcherServlet执行request对应的handler方法,HandlerAdapter本身不关注具体的handler执行逻辑。

DispatcherServlet中还有很多的其他组件可以参考官方文档

RequestMappingHandlerMapping

SpringMVC是如何将请求路由到Controller中的方法中的,分析RequestMappingHandlerMapping将会的到想要的答案。 从上面的类图中可以看出RequestMappingHandlerMapping实现了InitializingBean接口,也就是在RequestMappingHandlerMapping创建过程中属性设置后调用afterPropertiesSet方法。

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#afterPropertiesSet

@Override
public void afterPropertiesSet() {
    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);
    this.config.setContentNegotiationManager(getContentNegotiationManager());

    super.afterPropertiesSet();
}

该方法中又调用父类的afterProperitesSet()方法

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#afterPropertiesSet

@Override
public void afterPropertiesSet() {
    initHandlerMethods();
}

调用链入下图所示:

筛选IOC容器中是Controller的bean

AbstractHandlerMethodMapping#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.
        if (logger.isTraceEnabled()) {
            logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
        }
    }
    if (beanType != null && isHandler(beanType)) {
        detectHandlerMethods(beanName);
    }
}

该方法筛选出了是Controller的bean主要判断逻辑是isHandler()方法。

RequestMappingHandlerMapping#isHandler

@Override
protected boolean isHandler(Class<?> beanType) {
    return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
            AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

可以看出该方法主要判断了IOC容器中的bean是否有Controller.classRequestMapping.class注解。

HandlerMethod注册

AbstractHandlerMethodMapping#detectHandlerMethods

protected void detectHandlerMethods(Object handler) {
    Class<?> handlerType = (handler instanceof String ?
            obtainApplicationContext().getType((String) handler) : handler.getClass());

    if (handlerType != null) {
        Class<?> userType = ClassUtils.getUserClass(handlerType);
        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));
        }
        methods.forEach((method, mapping) -> {
            Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
            registerHandlerMethod(handler, invocableMethod, mapping);
        });
    }
}

上面的方法将Controller解析成了一个Map

最终将循环遍历这个Map并将RequestMappingInfo和对应的处理方法注册到内部类MappingRegistry中:

AbstractHandlerMethodMapping.MappingRegistry#register

public void register(T mapping, Object handler, Method method) {
    this.readWriteLock.writeLock().lock();
    try {
        HandlerMethod handlerMethod = createHandlerMethod(handler, method);
        assertUniqueMethodMapping(handlerMethod, mapping);
        this.mappingLookup.put(mapping, handlerMethod);

        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);
        }

        this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
    }
    finally {
        this.readWriteLock.writeLock().unlock();
    }
}

最后RequestMappingInfo和对应处理方法Method又将放入一个Map中只是这个Map的key为RequestMappingInfo,value为Method

在处理没次请求的时候都会交给org.springframework.web.servlet.DispatcherServlet#doDispatch方法处理,在该方法中就会根据请求的url对处理方法进行查找。最后使用反射调用该处理方法。