SpringMVC核心组件之(HandlerMapping)

HandlerMapping url映射,也就是访问的url,对应的处理器。

DispatcherServlet类中的initHandlerMappings()初始化HandlerMapping

SpringMVC默认有两个HandlerMapping

  1. BeanNameUrlHandlerMapping:只要beanName或别名是以/开头就会添加到该映射处理器中。
  2. RequestMappingHandlerMapping:@RequestMapping映射处理,最为常用的

先看看代码块,如果需要自定义,尽量将默认的也注入到容器中

    /** 检测所有HandlerMapping映射 还是只期望 “handlerMapping”bean?. */
    private boolean detectAllHandlerMappings = true;

    private void initHandlerMappings(ApplicationContext context) {
        this.handlerMappings = null;
        /**
         * 默认是 true
         * 如果 {@link #detectAllHandlerMappings} 设置为true,则从容器中查找所有  HandlerMapping类型的Bean
         */
        if (this.detectAllHandlerMappings) {
            Map<String, HandlerMapping> matchingBeans =
                    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
            if (!matchingBeans.isEmpty()) {
                /* 给当前 handlerMappings 赋值 */
                this.handlerMappings = new ArrayList<>(matchingBeans.values());
                AnnotationAwareOrderComparator.sort(this.handlerMappings);
            }
        }
        else {
            try {
                /* 如果 {@link #detectAllHandlerMappings} 设置为false  则从容器根据BeanName = handlerMapping 获取 */
                HandlerMapping hm = context.getBean("handlerMapping", HandlerMapping.class);
                /* 给当前 handlerMappings 赋值 */
                this.handlerMappings = Collections.singletonList(hm);
            }
            catch (NoSuchBeanDefinitionException ex) {
            }
        }

        /**
         * 如果没有自定义  则使用默认策略
         * {@link BeanNameUrlHandlerMapping}
         * {@link RequestMappingHandlerMapping}
         */
        if (this.handlerMappings == null) {
            this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
        }
    }
}
getDefaultStrategies(context, Class)

根据Class类型创建默认的实例,依靠Web容器的createBean创建实例

defaultStrategies 这里有一个静态变量,保存着SpringMVC的默认配置,查看默认配置文件

protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
        String key = strategyInterface.getName();
        /**
         * 重点关注下这里 SpringMVC 的默认策略
         */
        String value = defaultStrategies.getProperty(key);
        if (value != null) {
            /**
             * 以 逗号 分割
             */
            String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
            List<T> strategies = new ArrayList<>(classNames.length);
            for (String className : classNames) {
                try {
                    Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
                    /**
                     * 这里是用 Web容器去创建实例
                     * 默认是多实例的(scope = provided)
                     */
                    Object strategy = createDefaultStrategy(context, clazz);
                    strategies.add((T) strategy);
                }
                catch (ClassNotFoundException ex) {
                }
                catch (LinkageError err) {
                }
            }
            return strategies;
        }
        else {
            return new LinkedList<>();
        }
    }

createDefaultStrategy(context, clazz)

调用 Web容器的createBean创建Bean,直接从这里开始分析。


1:BeanNameUrlHandlerMapping

在这里插入图片描述

只要BeanName或Bean的别名是以/开头,都会添加到该映射器中。
createDefaultStrategy(context, BeanNameUrlHandlerMapping.class)

它在ApplicationContextAware的回调方法setApplicationContext中,进行处理 url映射。

在这里插入图片描述

public abstract class ApplicationObjectSupport implements ApplicationContextAware {
    @Override
    public final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException {
        /**
         * 忽略其他代码...
         */
        /**
         * 执行 ApplicationContext(context)  子类重写该方法了
         */
        initApplicationContext(context);
    }

    /**
     * 由子类 super.initApplicationContext(context); 执行到此
     *
     * 执行 initApplicationContext(); 无参的方法
     */
    protected void initApplicationContext(ApplicationContext context) throws BeansException {
        initApplicationContext();
    }
}

1.0.1:initApplicationContext()

public abstract class AbstractDetectingUrlHandlerMapping extends AbstractUrlHandlerMapping {
    @Override
    public void initApplicationContext() throws ApplicationContextException {
        /**
         * 初始化拦截器
         */
        super.initApplicationContext();
        /**
         * 注册容器所有的Bean的 url映射处理
         */
        detectHandlers();
    }
}

1.0.2:super.initApplicationContext()

public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
        implements HandlerMapping, Ordered, BeanNameAware {
    @Override
    protected void initApplicationContext() throws BeansException {
        /**
         * 注册格外的拦截器
         */
        extendInterceptors(this.interceptors);
        detectMappedInterceptors(this.adaptedInterceptors);
        initInterceptors();
    }
    /* 由子类拓展 */
    protected void extendInterceptors(List<Object> interceptors) {
    }
    /* 从容器中获取所有的 MappedInterceptor类型的拦截器 */
    protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
        mappedInterceptors.addAll(
                BeanFactoryUtils.beansOfTypeIncludingAncestors(
                        obtainApplicationContext(), MappedInterceptor.class, true, false).values());
    }
    /* 初始化指定的拦截器 */
    protected void initInterceptors() {
        if (!this.interceptors.isEmpty()) {
            for (int i = 0; i < this.interceptors.size(); i++) {
                Object interceptor = this.interceptors.get(i);
                if (interceptor == null) {
                    throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
                }
                this.adaptedInterceptors.add(adaptInterceptor(interceptor));
            }
        }
    }
}

1.0.3:detectHandlers()

注册容器中所有的 url映射处理器

public abstract class AbstractDetectingUrlHandlerMapping extends AbstractUrlHandlerMapping {
    /**
     * 注册容器中所有的 url映射
     */
    protected void detectHandlers() throws BeansException {
        ApplicationContext applicationContext = obtainApplicationContext();
        /* 从容器中获取所有的 BeanName */
        String[] beanNames = (this.detectHandlersInAncestorContexts ?
                BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
                applicationContext.getBeanNamesForType(Object.class));

        // 取任何我们可以确定网址的bean名称。
        for (String beanName : beanNames) {
            /* 是否满足条件 */
            String[] urls = determineUrlsForHandler(beanName);
            if (!ObjectUtils.isEmpty(urls)) {
                // 找到的URL路径:让我们把它看作一个处理程序。
                registerHandler(urls, beanName);
            }
        }
    }
}

1.0.4:determineUrlsForHandler(beanName)

该BeanName 是否是 url映射处理

public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {
    /**
     * 检查给定bean的名称和别名,以“/”开头。
     */
    @Override
    protected String[] determineUrlsForHandler(String beanName) {
        List<String> urls = new ArrayList<>();
        if (beanName.startsWith("/")) {
            urls.add(beanName);
        }
        String[] aliases = obtainApplicationContext().getAliases(beanName);
        for (String alias : aliases) {
            if (alias.startsWith("/")) {
                urls.add(alias);
            }
        }
        return StringUtils.toStringArray(urls);
    }
}


2:RequestMappingHandlerMapping

@RequestMapping url映射处理
InitializingBeanBean初始化方法afterPropertiesSet中,添加容器中所有的@RequestMapping
在这里插入图片描述

2.0.0:AbstractHandlerMethodMapping.afterPropertiesSet()

Bean的初始化方法

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
    /**
     * 检查所有的 Handler 方法 可以url映射的方法
     * @see #initHandlerMethods
     */
    @Override
    public void afterPropertiesSet() {
        initHandlerMethods();
    }
    /**
     * 遍历 容器中所有的 BeanName
     */
    protected void initHandlerMethods() {
        for (String beanName : getCandidateBeanNames()) {
            /* 排除 {scopedTarget.} 开头的BeanName */
            if (!beanName.startsWith("scopedTarget.")) {
                /**
                 * 处理该Bean是否满足条件
                 * 重点关注这里
                 */
                processCandidateBean(beanName);
            }
        }
        /*  */
        handlerMethodsInitialized(getHandlerMethods());
    }
    protected void handlerMethodsInitialized(Map<T, HandlerMethod> handlerMethods) {
        // 总计包括检测到的映射+通过注册映射的显式注册
        int total = handlerMethods.size();
        if ((logger.isTraceEnabled() && total == 0) || (logger.isDebugEnabled() && total > 0) ) {
            logger.debug(total + " mappings in " + formatMappingName());
        }
    }
}
2.0.1:processCandidateBean(beanName)

处理当前BeanName是否满足 url映射,如果满足,则添加到 url映射集合中

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
    protected void processCandidateBean(String beanName) {
        Class<?> beanType = null;
        try {
            /* 获取 Bean的类型 */
            beanType = obtainApplicationContext().getType(beanName);
        }
        catch (Throwable ex) {
        }
        /**
         * isHandler 检查是否有 @Controller 或 @RequestMapping 注解
         */
        if (beanType != null && isHandler(beanType)) {
            /* 检查该 Bean */
            detectHandlerMethods(beanName);
        }
    }
    protected void detectHandlerMethods(Object handler) {
        /* 如果 handler是BeanName  获取 Bean的类型 */
        Class<?> handlerType = (handler instanceof String ?
                obtainApplicationContext().getType((String) handler) : handler.getClass());

        if (handlerType != null) {
            Class<?> userType = ClassUtils.getUserClass(handlerType);
            /**
             * 获取Bean中 所有标注了  @RequestMapping 注解的 方法
             *
             * T = RequestMappingInfo   包装了 RequestMapping信息
             */
            Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                    (MethodIntrospector.MetadataLookup<T>) method -> {
                        try {
                            return getMappingForMethod(method, userType);
                        }
                        catch (Throwable ex) {
                        }
                    });
            methods.forEach((method, mapping) -> {
                Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
                /**
                 * 添加到 url映射的集合中
                 */
                registerHandlerMethod(handler, invocableMethod, mapping);
            });
        }
    }
}

总结:

HandlerMapping是处理url映射的,所有的url映射需要在HandlerMapping的映射集合中。如果在HandlerMapping中,找不到url,则404.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Me_Liu_Q

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值