前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringMVC之细说HandlerMapping

SpringMVC之细说HandlerMapping

作者头像
一觉睡到小时候
发布2019-07-04 16:39:31
7900
发布2019-07-04 16:39:31
举报
文章被收录于专栏:国产程序员国产程序员

DispatcherServlet与HandlerMapping关系

客户端发送请求,web容器接受请求,如果请求与DispatcherServlet的请求映射路径(url-pattern)匹配,web容器将请求交给DispatcherServlet处理。DispatcherServlet在加载的过程中会对HandlerMapping进行初始化,(具体可参照SpringMVC源码中DispatherServlet类的initHandlerMappings(ApplicationContextcontext)方法)。DispatcherServlet初始化完成后会自动扫描applicationContext.xml中的bean,根据名称(也就是bean的id或者类型来查找,如果找到则使用这个bean,找不到则使用DispatcherServlet.properties中的默认组件.

handlerMapping初始化

代码语言:javascript
复制
简单来说就是用来存储所有URL与处理类的map关系,以及在请求过来的时候根据输入URL
匹配到对应的处理类
代码语言:javascript
复制
第一步:
代码语言:javascript
复制
DefaultAnnotationHandlerMapping继承了AbstractDetectingUrlHandlerMapping,
在AbstractDetectingUrlHandlerMapping中遍历所有的DispatcherServlet配置文件中的
所有bean,找到相应的URL并进行缓存
代码语言:javascript
复制
protected void detectHandlers() throws BeansException {
代码语言:javascript
复制
        if (logger.isDebugEnabled()) {
代码语言:javascript
复制
        logger.debug("Looking for URL mappings in application context: " 
     
                            + getApplicationContext());
代码语言:javascript
复制
        }
代码语言:javascript
复制
        //获取了所有的bean,值得注意的是此处仅取出所有DispatcherServlet相关配置
              //文件涉及的所有类
代码语言:javascript
复制
        String[] beanNames = (this.detectHandlersInAncestorContexts ?
代码语言:javascript
复制
        BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), 
                                                            Object.class) :
代码语言:javascript
复制
        getApplicationContext().getBeanNamesForType(Object.class));
代码语言:javascript
复制
        // 遍历
代码语言:javascript
复制
        for (String beanName : beanNames) {
代码语言:javascript
复制
        //拿到Controller 上配置的所有URL
代码语言:javascript
复制
        String[] urls = determineUrlsForHandler(beanName);
代码语言:javascript
复制
        if (!ObjectUtils.isEmpty(urls)) {
代码语言:javascript
复制
        // 将所有的 url与controller对应关系存储下来
代码语言:javascript
复制
        registerHandler(urls, beanName);
代码语言:javascript
复制
        }else {
代码语言:javascript
复制
        if (logger.isDebugEnabled()) {
代码语言:javascript
复制
        logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
代码语言:javascript
复制
        }
代码语言:javascript
复制
        }
代码语言:javascript
复制
        }
代码语言:javascript
复制
        }
代码语言:javascript
复制
第二步:
代码语言:javascript
复制
看一下这个类:DefaultAnnotationHandlerMapping,其中
代码语言:javascript
复制
determineUrlsForHandler(String beanName)和
determineUrlsForHandlerMethods(Class<?> handlerType, 
final boolean hasTypeLevelMapping)
代码语言:javascript
复制
解析bean的@RequestMapping,
代码语言:javascript
复制
protected String[] determineUrlsForHandler(String beanName) {
代码语言:javascript
复制
        ApplicationContext context = getApplicationContext();
代码语言:javascript
复制
        Class<?> handlerType = context.getType(beanName);
代码语言:javascript
复制
        //找到对应bean 类级别标注的所有的 @RequestMapping
代码语言:javascript
复制
        RequestMapping mapping = context.findAnnotationOnBean(beanName, RequestMapping.class);
代码语言:javascript
复制
        if (mapping != null) {
代码语言:javascript
复制
        this.cachedMappings.put(handlerType, mapping);
代码语言:javascript
复制
        Set<String> urls = new LinkedHashSet<String>();
代码语言:javascript
复制
        String[] typeLevelPatterns = mapping.value();
代码语言:javascript
复制
        if (typeLevelPatterns.length > 0) {
代码语言:javascript
复制
        // 找到对应bean 方法级别的所有 @RequestMapping 
代码语言:javascript
复制
        String[] methodLevelPatterns = determineUrlsForHandlerMethods(handlerType, true);
代码语言:javascript
复制
        for (String typeLevelPattern : typeLevelPatterns) {
代码语言:javascript
复制
        if (!typeLevelPattern.startsWith("/")) {
代码语言:javascript
复制
        typeLevelPattern = "/" + typeLevelPattern;
代码语言:javascript
复制
        }
代码语言:javascript
复制
        boolean hasEmptyMethodLevelMappings = false;
代码语言:javascript
复制
        for (String methodLevelPattern : methodLevelPatterns) {
代码语言:javascript
复制
        if (methodLevelPattern == null) {
代码语言:javascript
复制
        hasEmptyMethodLevelMappings = true;
代码语言:javascript
复制
        }else {
代码语言:javascript
复制
        String combinedPattern = getPathMatcher().combine(typeLevelPattern, methodLevelPattern);
代码语言:javascript
复制
        //将类级别@RequestMapping和方法级别的@RequestMapping组合放入list 
代码语言:javascript
复制
        addUrlsForPath(urls, combinedPattern);
代码语言:javascript
复制
        }
代码语言:javascript
复制
        }
代码语言:javascript
复制
        if (hasEmptyMethodLevelMappings ||
代码语言:javascript
复制
        org.springframework.web.servlet.mvc.Controller.class.isAssignableFrom(handlerType)) {
代码语言:javascript
复制
        addUrlsForPath(urls, typeLevelPattern);
代码语言:javascript
复制
        }
代码语言:javascript
复制
        }
代码语言:javascript
复制
        return StringUtils.toStringArray(urls);
代码语言:javascript
复制
        }else {
代码语言:javascript
复制
        // actual paths specified by @RequestMapping at method level
代码语言:javascript
复制
        return determineUrlsForHandlerMethods(handlerType, false);
代码语言:javascript
复制
        }
代码语言:javascript
复制
        }
代码语言:javascript
复制
        else if (AnnotationUtils.findAnnotation(handlerType, Controller.class) != null) {
代码语言:javascript
复制
        // @RequestMapping to be introspected at method level
代码语言:javascript
复制
        return determineUrlsForHandlerMethods(handlerType, false);
代码语言:javascript
复制
        }else {
代码语言:javascript
复制
        return null;
代码语言:javascript
复制
        }
代码语言:javascript
复制
        }
代码语言:javascript
复制
第三步:
代码语言:javascript
复制
AbstractUrlHandlerMapping.registerHandler(String urlPath, Object handler)
代码语言:javascript
复制
将url 和 controller放入缓存,后续处理url请求时将会用到。
代码语言:javascript
复制
protected void registerHandler(String urlPath, Object handler) 
                   throws BeansException, IllegalStateException {
代码语言:javascript
复制
    Assert.notNull(urlPath, "URL path must not be null");
代码语言:javascript
复制
    Assert.notNull(handler, "Handler object must not be null");
代码语言:javascript
复制
    Object resolvedHandler = handler;
代码语言:javascript
复制
    if (!this.lazyInitHandlers && handler instanceof String) {
代码语言:javascript
复制
        String handlerName = (String)handler;
代码语言:javascript
复制
        if (this.getApplicationContext().isSingleton(handlerName)) {
代码语言:javascript
复制
            resolvedHandler = this.getApplicationContext().getBean(handlerName);
代码语言:javascript
复制
        }
代码语言:javascript
复制
    }
代码语言:javascript
复制
    Object mappedHandler = this.handlerMap.get(urlPath);
代码语言:javascript
复制
    if (mappedHandler != null) {
代码语言:javascript
复制
        if (mappedHandler != resolvedHandler) {
代码语言:javascript
复制
            throw new IllegalStateException("Cannot map "  
                       this.getHandlerDescription(handler) + 
                       " to URL path [" + urlPath + "]: There is already " +
                       this.getHandlerDescription(mappedHandler) + " mapped.");
代码语言:javascript
复制
        }
代码语言:javascript
复制
    } else if (urlPath.equals("/")) {
代码语言:javascript
复制
        if (this.logger.isInfoEnabled()) {
代码语言:javascript
复制
            this.logger.info("Root mapping to " + this.getHandlerDescription(handler));
代码语言:javascript
复制
        }
代码语言:javascript
复制
        this.setRootHandler(resolvedHandler);
代码语言:javascript
复制
    } else if (urlPath.equals("/*")) {
代码语言:javascript
复制
        if (this.logger.isInfoEnabled()) {
代码语言:javascript
复制
            this.logger.info("Default mapping to " + this.getHandlerDescription(handler));
代码语言:javascript
复制
        }
代码语言:javascript
复制
        this.setDefaultHandler(resolvedHandler);
代码语言:javascript
复制
    } else {
代码语言:javascript
复制
        this.handlerMap.put(urlPath, resolvedHandler);
代码语言:javascript
复制
        if (this.logger.isInfoEnabled()) {
代码语言:javascript
复制
            this.logger.info("Mapped URL path [" + urlPath + "] onto " + 
                                       this.getHandlerDescription(handler));
代码语言:javascript
复制
        }
代码语言:javascript
复制
    }
代码语言:javascript
复制
}
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-06-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 国产程序员 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档