Spring MVC 之通过URL获取Controller

1.概念

跟请求处理有关的三个概念

  • Handlder: 处理器,直接对应的是Spring MVC的Controller层,它的具体表现形式有很多,
    可以是类,也可以是方法,也可以是别的表现形式。标注了@RequestMapping的所有方法都可以看成是一个Handler

  • HandlerMapping:用来查找Handler,在Spring MVC中会处理很多请求,每个请求都需要一
    个Handler来处理。

  • HandlerAdapter:因为Spring MVC中的Handler可以是任意的形式,只要能处理请求就可
    以,但是Servlet需要处理方法的结构是固定的,都是以request和response为参数的方法,
    所以,需要一个适配器来用固定结构的Servlet处理方法调用灵活的Handler来进行处理。

2.HandlerMapping

HandlerMapping的作用是根据request找到相应的处理器Handler和Interceptor,HandlerMapping的接口只有一个方法

 //方法的实现非常简单,通过request参数返回HandlerExecutionChain
 HandlerExecutionChain getHandler(HttpServletRequest var1) throws Exception;
 

DispatcherServlet中查找getHandler的实现

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        //查找Handler是通过迭代器来遍历所有的HandlerMapping,当找到一个
        HandlerExecutionChain,就立即返回
        Iterator var2 = this.handlerMappings.iterator();

        HandlerExecutionChain handler;
        do {
            if(!var2.hasNext()) {
                return null;
            }

            HandlerMapping hm = (HandlerMapping)var2.next();
            if(this.logger.isTraceEnabled()) {
                this.logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name \'" + this.getServletName() + "\'");
            }
            
            //当handler不为空时,调出循环,返回handler
            handler = hm.getHandler(request);
        } while(handler == null);

        return handler;
    }

2.1.AbstractHandlerMapping

AbstractHandlerMapping是HandlerMapping的抽象实现,所有HandlerMapping都继承自AbstractHandlerMapping。AbstractHandlerMapping采用模板模式设计了HandlerMapping实现的整体结构,子类只需要通过模板方法提供一些具体的实现就行了。

2.1.1 AbstractHandlerMapping初始化

AbstractHandlerMapping的创建就是在initApplicationContext方法里面实现的

protected void initApplicationContext() throws BeansException {
    //用于给子类提供一个添加(或修改)Interceptors的入口
    this.extendInterceptors(this.interceptors);
    this.detectMappedInterceptors(this.mappedInterceptors);
    this.initInterceptors();
}

//detectMappedInterceptors方法将SpringMVC容器及父容器中的所有
MappedInterceptor类型的Bean添加到mappedInterceptors属性
protected void detectMappedInterceptors(List<MappedInterceptor> mappedInterceptors) {
        mappedInterceptors.addAll(BeanFactoryUtils.beansOfTypeIncludingAncestors(this.getApplicationContext(), MappedInterceptor.class, true, false).values());
    }

//initInterceptors方法作用是初始化Interceptor,具体内容是将interceptors属性所包含的对象按类型添加到mappedInterceptors或者adaptedInterceptors

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

            if(interceptor instanceof MappedInterceptor) {
                this.mappedInterceptors.add((MappedInterceptor)interceptor);
            } else {
                this.adaptedInterceptors.add(this.adaptInterceptor(interceptor));
            }
        }
    }
}

AbstractHandlerMapping中的Interceptor有三个List类型的属性:interceptors、mappedInterceptors和adaptedInterceptors

  • Interceptors: 用于配置SpringMVC的拦截器,有两种设置方式:1、注册HandlerMa-
    pping时通过属性设置;2、通过子类的extendInterceptors钩子方法进行设置。Interceptors并不会直接使用,而是通过initInterceptors方法按类型分配到mappedInterceptors和adaptedInterceptors中进行使用,Interceptors只用于配置。

  • mappedInterceptors:此类Interceptors在使用时需要与请求的url进行匹配,只有匹配成功后才会添加到getHandler的返回值HandlerExecutionChain里。它有两种获取途径:从interceptors获取或者注册到spring的容器中通过detectMappedInterceptors方法获取

  • adaptedInterceptors: 这种类型的Interceptors不需要进行匹配,在getHandler中
    会全部添加到返回值HandlerExecutionChain里面。它只能从interceptors中获取。

2.1.2.AbstractHandlerMapping的用途

HandlerMapping是通过getHandler方法来获取处理器Handler和拦截器Interceptor的

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    //获取handler,getHandlerInternal是模板方法,主要由子类来实现    
    Object handler = this.getHandlerInternal(request);
    //使用默认的Handler,默认的Handler保存在AbstractHandlerMapping的一个Object类型的属性defaultHandler中,可以在配置HandlerMapping时进行配置,也可以在子类中进行设置
    if(handler == null) {
        handler = this.getDefaultHandler();
    }

    if(handler == null) {
        return null;
    } else {
        //如果找到的Handler是String类型,则以它为名到Spring MVC的容器里查找相应的Bean
        if(handler instanceof String) {
            String handlerName = (String)handler;
            handler = this.getApplicationContext().getBean(handlerName);
        }
        
        
        return this.getHandlerExecutionChain(handler, request);
    }
}

//查找HandlerExecutionChain的方法
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
    //创建HandlerExecutionChain类型的变量
    HandlerExecutionChain chain = handler instanceof HandlerExecutionChain?(HandlerExecutionChain)handler:new HandlerExecutionChain(handler);
    
    //然后将符合类型的adaptedInterceptors和mappedInterceptor添加到HandlerExecutionChain
    chain.addInterceptors(this.getAdaptedInterceptors());
    String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
    Iterator var5 = this.mappedInterceptors.iterator();

    while(var5.hasNext()) {
        MappedInterceptor mappedInterceptor = (MappedInterceptor)var5.next();
        if(mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
            chain.addInterceptor(mappedInterceptor.getInterceptor());
        }
    }

    return chain;
}
2.1.3.Map(保存url和Handler)初始化

afterPropertiesSet方法是bean初始化完成之后,执行的方法,要求bean要实现InitializingBean接口


public void afterPropertiesSet() {
    this.initHandlerMethods();
}

protected void registerHandlerMethod(Object handler, Method method, T mapping) {
    HandlerMethod newHandlerMethod = this.createHandlerMethod(handler, method);
    HandlerMethod oldHandlerMethod = (HandlerMethod)this.handlerMethods.get(mapping);
    if(oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod)) {
        throw new IllegalStateException("Ambiguous mapping found. Cannot map \'" + newHandlerMethod.getBean() + "\' bean method \n" + newHandlerMethod + "\nto " + mapping + ": There is already \'" + oldHandlerMethod.getBean() + "\' bean method\n" + oldHandlerMethod + " mapped.");
    } else {
        this.handlerMethods.put(mapping, newHandlerMethod);
        if(this.logger.isInfoEnabled()) {
            this.logger.info("Mapped \"" + mapping + "\" onto " + newHandlerMethod);
        }

        Set patterns = this.getMappingPathPatterns(mapping);
        Iterator name = patterns.iterator();

        while(name.hasNext()) {
            String pattern = (String)name.next();
            if(!this.getPathMatcher().isPattern(pattern)) {
                this.urlMap.add(pattern, mapping);
            }
        }

        if(this.namingStrategy != null) {
            String name1 = this.namingStrategy.getName(newHandlerMethod, mapping);
            this.updateNameMap(name1, newHandlerMethod);
        }

    }
}    
    

request请求中的url部门包括方法的@RequestMapping的和类的@RequestMapping注解,类中的注解会在下面的方法中做处理

protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
    RequestMappingInfo info = null;
    RequestMapping methodAnnotation = (RequestMapping)AnnotationUtils.findAnnotation(method, RequestMapping.class);
    if(methodAnnotation != null) {
        //判断方法上面是否有注解
        RequestCondition methodCondition = this.getCustomMethodCondition(method);
        info = this.createRequestMappingInfo(methodAnnotation, methodCondition);
        //判读类上面是否有注解,如果有则将类上面的注解和方法上面的注解做combine处理
        RequestMapping typeAnnotation = (RequestMapping)AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
        if(typeAnnotation != null) {
            RequestCondition typeCondition = this.getCustomTypeCondition(handlerType);
            info = this.createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);
        }
    }

    return info;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容

  • Spring MVC一、什么是 Spring MVCSpring MVC 属于 SpringFrameWork 的...
    任任任任师艳阅读 3,369评论 0 32
  • 1、Spring MVC请求流程 (1)初始化:(对DispatcherServlet和ContextLoderL...
    拾壹北阅读 1,944评论 0 12
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,579评论 18 139
  • 1. 配置 springMVC的核心是DispatcherServlet,它实现了HttpServlet类,在we...
    aaron1993阅读 1,279评论 0 0
  • photo reading照相式阅读法。 快,每天读5-10本书,开课有利于知识的输出。头一天的分享很重要,主题:...
    浮生若梦弦阅读 125评论 0 0