美文网首页spring framework
[Spring MVC]HandlerMapping的初始化

[Spring MVC]HandlerMapping的初始化

作者: AbstractCulture | 来源:发表于2021-06-27 19:57 被阅读0次

    HttpServletBean#init

    容器初始化DispatcherServlet这个Servlet实例的时候,会调用其init()方法(该方法在HttpServletBean中),HttpServletBean会执行子类的initServletBean方法,在这里,FrameworkServlet会进行容器的初始化,在容器的refresh执行到finishRefresh的时候,会发布事件,最终会激活FrameworkServlet#onApplicationEvent,最终就会执行到org.springframework.web.servlet.DispatcherServlet#initStrategies中进行MVC组件的初始化.
    下面来看IDEA的执行栈:

    执行栈

    初始化MVC的九大组件

    • org.springframework.web.servlet.DispatcherServlet#initStrategies
    protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        initHandlerMappings(context);
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);
        initFlashMapManager(context);
    }
    
    • MultipartResolver
      MultipartResolver主要用来处理文件上传请求,它会将请求包装成MultipartHttpServletRuest实例,通过它的抽象子类AbstractMultipartHttpServletRequest可以看到很多跟文件相关的方法.例如:
    @Override
    public Iterator<String> getFileNames() {
        return getMultipartFiles().keySet().iterator();
    }
    
    @Override
    public MultipartFile getFile(String name) {
        return getMultipartFiles().getFirst(name);
    }
    
    @Override
    public List<MultipartFile> getFiles(String name) {
        List<MultipartFile> multipartFiles = getMultipartFiles().get(name);
        if (multipartFiles != null) {
            return multipartFiles;
        }
        else {
            return Collections.emptyList();
        }
    }
    
    @Override
    public Map<String, MultipartFile> getFileMap() {
        return getMultipartFiles().toSingleValueMap();
    }
    
    • LocaleResolver
      视图渲染组件ViewResolverresolveViewName方法需要传输一个Locale实例,这个实例对象由LocaleResovler进行解析。这也是Spring MVC对国际化的支持.

    • ThemeResolver

    用于进行主题渲染

    • HandlerMapping
      核心组件,它会将被@RequestMapping注解标记的Controller解析成HandlerMapping实例,在HandMapping中声明了一个getHandler方法,在处理请求的时候,MVC会用这个方法找到匹配的处理方法。
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
    
    • HandlerAdapters
      这是一个Handler的适配器,Handler可以以任意的形式存在,但是servlet请求都是以doService(HttpServletRequest req,HttpServletResponse resp)请求的,要让固定的Servlet方法调用Handler进行处理,这就是适配器要做的事情。

    • HandlerExceptionResolvers
      HandlerExceptionResolvers是用来处理Handler产生的异常组件。它会根据异常设置ModelAndView,然后交由渲染器进行渲染。

    • ViewResovler
      渲染视图,Spring MVC最终返回的是View类型的视图。如jsp,就需要使用到.

    • RequestToViewNameTranslator
      RequestToViewNameTranslator的作用是从请求中获取ViewName,因为ViewResovler根据ViewName查找View.如果Handler没有指定返回的ViewName,交由该组件处理.

    • FlashMapManager
      FlashMap用于传递重定向的参数。

    HanlderMapping的初始化

    用户的请求经过DispatcherServlet会根据HandlerMapping来定位到具体的Controller#method.当容器启动的时候,会对标记了@RqequestMapping@Controller的Bean进行HandlerMapping映射的创建。

    初始化RequestMappingHandlerMapping

    • UML
    UML

    RequestMappingHandlerMapping实现了许多的Aware接口,可以从容器中获取ApplicationContextServletContextBeanNameEmbeddedValueResovler.同时,RequestMappingHandlerMapping继承的AbstractHandlerMethodMapping还实现了IoC的生命周期回调函数InitializingBean.

    • org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#afterPropertiesSet
    /**
     * Detects handler methods at initialization.
     * @see #initHandlerMethods
     */
    @Override
    public void afterPropertiesSet() {
        initHandlerMethods();
    }
    

    在IoC进行doCreateBean的时候,会回调InitializingBean#afterPropertiesSet函数.我们进入这个函数看看执行了什么逻辑.

    • org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#initHandlerMethods
    /**
     * Scan beans in the ApplicationContext, detect and register handler methods.
     * @see #getCandidateBeanNames()
     * @see #processCandidateBean
     * @see #handlerMethodsInitialized
     */
    protected void initHandlerMethods() {
        // 遍历容器中所有的beanName
        for (String beanName : getCandidateBeanNames()) {
            // 如果beanName以“scopedTarget.”开头,忽略
            // 通常这些代理Bean的scope都为(session、application、request)
            if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                processCandidateBean(beanName);
            }
        }
        handlerMethodsInitialized(getHandlerMethods());
    }
    

    从容器中获取所有的beanName集合,遍历beanNames.
    对beanName不以"scopedTarget."开头的bean进行处理.

    • org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#processCandidateBean
    protected void processCandidateBean(String beanName) {
        Class<?> beanType = null;
        try {
            // 获取bean对应的class对象
            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);
            }
        }
        // 判断class对象上是否有@Controller和@RequestMapping的注解
        if (beanType != null && isHandler(beanType)) {
            // 提取其url与controller映射关系
            detectHandlerMethods(beanName);
        }
    }
    
    1. 通过beanName获取该bean的Type.
    2. 判断class对象上是否有@Controller和@RequestMapping的注解,提取其url与method的映射关系.
    • org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#detectHandlerMethods
        /**
         * Look for handler methods in the specified handler bean.
         * @param handler either a bean name or an actual handler instance
         * @see #getMappingForMethod
         */
        protected void detectHandlerMethods(Object handler) {
            // 如果handler是字符串,证明是一个beanName,则从IoC容器中获取其class对象;
            // 否则直接获取class对象
            Class<?> handlerType = (handler instanceof String ?
                    obtainApplicationContext().getType((String) handler) : handler.getClass());
    
            if (handlerType != null) {
                // 为了确保获取到的类是被代理的类
                Class<?> userType = ClassUtils.getUserClass(handlerType);
                // 寻找方法上有@RequestMapping注解的Method实例
                // 注意这里的methods是一个map,key为method实例,value是RequestMappingHandlerMapping实例
                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对象依次注册到HandlerMapping中
                methods.forEach((method, mapping) -> {
                    // 获取被代理的方法实例
                    Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
                    registerHandlerMethod(handler, invocableMethod, mapping);
                });
            }
        }
    
    1. 获取需要处理的class对象.
    2. 如果当前bean是代理类,需要获取被代理的类,也就是TargetClass.
    3. 通过MethodIntrospector#selectMethods对当前class中的methods进行遍历,寻找方法上有@RequestMapping注解的Method实例,对其进行解析,随后放入methodMap这个容器中.key为method实例,value是RequestMappingInfo实例
    4. methods进行遍历,在前面获取到的被代理类,现在需要转换成代理类的方法实例.随后对当前方法进行注册.
    • org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#getMappingForMethod
    protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
        // 根据当前方法解析出RequestMappingInfo实例
        RequestMappingInfo info = createRequestMappingInfo(method);
        if (info != null) {
            // 创建类上面的RequestMapping信息
            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;
    }
    
    1. 解析method的注解,构造出RequestMappingInfo实例.
    2. 解析class上的RequestMappingInfo实例.
    3. 将两个RequestMappingInfo进行合并.
    • org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#createRequestMappingInfo(java.lang.reflect.AnnotatedElement)
    @Nullable
    private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
        // 如果该函数含有@RequestMapping注解,则解析该注解的信息
        // 否则返回null
        // 关键方法: createRequestMappingInfo
        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);
    }
    
    1. 如果该函数含有@RequestMapping注解,则解析该注解的信息
    2. 进入重载的createRequestMappingInfo.
    • org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#createRequestMappingInfo(org.springframework.web.bind.annotation.RequestMapping, org.springframework.web.servlet.mvc.condition.RequestCondition<?>)
    protected RequestMappingInfo createRequestMappingInfo(
            RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
        // 使用builder模式进行参数构建
        RequestMappingInfo.Builder builder = RequestMappingInfo
                // 支持SPEL表达式的解析
                // RequestMappingHandlerMapping实现了EmbeddedValueResolverAware接口
                .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();
    }
    

    注册映射关系

    • org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#register
    public void register(T mapping, Object handler, Method method) {
        this.readWriteLock.writeLock().lock();
        try {
            // 创建HandlerMethod实例
            HandlerMethod handlerMethod = createHandlerMethod(handler, method);
            // 验证方法的唯一性,也就是当前方法映射关系是否已经注册过了
            assertUniqueMethodMapping(handlerMethod, mapping);
            // 注册RequestMappingInfo和HandlerMethod
            this.mappingLookup.put(mapping, handlerMethod);
            // 注册请求路径和对应的RequestMappingInfo
            List<String> directUrls = getDirectUrls(mapping);
            for (String url : directUrls) {
                this.urlLookup.add(url, mapping);
            }
    
            String name = null;
            if (getNamingStrategy() != null) {
                // 注册请求路径和HandlerMethod的映射
                name = getNamingStrategy().getName(handlerMethod, mapping);
                addMappingName(name, handlerMethod);
            }
    
            CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
            if (corsConfig != null) {
                // 注册HandlerMethod与跨域信息的映射
                this.corsLookup.put(handlerMethod, corsConfig);
            }
            // 创建以及注册MappingRegistration信息
            this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
        }
        finally {
            this.readWriteLock.writeLock().unlock();
        }
    }
    
    1. 上读写锁,防止并发引发的线程不安全问题
    2. 创建HandlerMethod实例
    3. 验证方法的唯一性,也就是当前方法映射关系是否已经注册过了
    4. 注册RequestMappingInfo和HandlerMethod
    5. 注册请求路径和对应的RequestMappingInfo
    6. 注册HandlerMethod与跨域信息的映射
    7. 解锁
    • org.springframework.web.method.HandlerMethod
    public class HandlerMethod {
    
        /** Logger that is available to subclasses. */
        protected final Log logger = LogFactory.getLog(getClass());
    
        private final Object bean;
    
        @Nullable
        private final BeanFactory beanFactory;
        /**
         * Controller类型
         */
        private final Class<?> beanType;
        /**
         * 方法实例
         */
        private final Method method;
    
        private final Method bridgedMethod;
        /**
         * 方法参数数组
         */
        private final MethodParameter[] parameters;
    
        @Nullable
        private HttpStatus responseStatus;
    
        @Nullable
        private String responseStatusReason;
    
        @Nullable
        private HandlerMethod resolvedFromHandlerMethod;
    
        @Nullable
        private volatile List<Annotation[][]> interfaceParameterAnnotations;
    }
    

    DispatcherServlet对HandleMapping的初始化

    • org.springframework.web.servlet.DispatcherServlet#initHandlerMappings
    private void initHandlerMappings(ApplicationContext context) {
        this.handlerMappings = null;
        // 是否检查所有的HandlerMapping实现类并加载,默认为true
        if (this.detectAllHandlerMappings) {
            // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
            // 寻找IoC容器中HandlerMapping类型的Bean实例
            Map<String, HandlerMapping> matchingBeans =
                    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
            if (!matchingBeans.isEmpty()) {
                this.handlerMappings = new ArrayList<>(matchingBeans.values());
                // We keep HandlerMappings in sorted order.
                // 对HandlerMapping列表进行排序
                AnnotationAwareOrderComparator.sort(this.handlerMappings);
            }
        }
        else {
            try {
                // 从容器中获取beanName为handlerMapping的Bean
                HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
                this.handlerMappings = Collections.singletonList(hm);
            }
            catch (NoSuchBeanDefinitionException ex) {
                // Ignore, we'll add a default HandlerMapping later.
            }
        }
    
        // Ensure we have at least one HandlerMapping, by registering
        // a default HandlerMapping if no other mappings are found.
        if (this.handlerMappings == null) {
            this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
            if (logger.isTraceEnabled()) {
                logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
                        "': using default strategies from DispatcherServlet.properties");
            }
        }
    }
    
    1. 这里会从容器中找到实现了HandlerMapping接口的bean.进行排序后赋值给handlerMappings变量.
      RequestMappingHandlerMapping实现了这个接口,并进行了初始化注册进了容器中.
      因此,此处就建立了DispatcherServletRequestMappingHandlerMapping之间的联系.

    相关文章

      网友评论

        本文标题:[Spring MVC]HandlerMapping的初始化

        本文链接:https://www.haomeiwen.com/subject/cggxultx.html