欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

 SpringMvc流程

程序员文章站 2024-01-25 20:10:40
RequestMappingHandlerMapping是SpringMvc中一个比较核心的类,查看下它的类结构图: InitializingBean是个很神奇的接口,在Spring每个容器的bean构造方法、属性设置之后,会先调用InitializingBean接口的afterProperties ......

 requestmappinghandlermapping是springmvc中一个比较核心的类,查看下它的类结构图:  

           SpringMvc流程

initializingbean是个很神奇的接口,在spring每个容器的bean构造方法、属性设置之后,会先调用initializingbean接口的afterpropertiesset方法;

requestmappinghandlermapping的afterpropertiesset方法: 初始化了config对象,以及调用父类abstracthandlermethodmapping的afterpropertiesset,父类方法afterpropertiesset 逻辑是 inithandlermethods,这也是springmvc初始化寻找controller以及映射加载的核心逻辑; 

@override
    public void afterpropertiesset() {
        this.config = new requestmappinginfo.builderconfiguration();
        this.config.setpathhelper(geturlpathhelper());
        this.config.setpathmatcher(getpathmatcher());
        this.config.setsuffixpatternmatch(this.usesuffixpatternmatch);
        this.config.settrailingslashmatch(this.usetrailingslashmatch);
        this.config.setregisteredsuffixpatternmatch(this.useregisteredsuffixpatternmatch);
        this.config.setcontentnegotiationmanager(getcontentnegotiationmanager());
     // 初始化config对象,主要属性就是pathmatcher; 以及调用父类 afterpropertiesset 方法,这是springmvc映射关系加载的核心;
        super.afterpropertiesset();
    }

 

abstracthandlermethodmapping 的 inithandlermethods代码:

protected void inithandlermethods() {
        if (logger.isdebugenabled()) {
            logger.debug("looking for request mappings in application context: " + getapplicationcontext());
        }
        string[] beannames = (this.detecthandlermethodsinancestorcontexts ?
                beanfactoryutils.beannamesfortypeincludingancestors(getapplicationcontext(), object.class) :
                getapplicationcontext().getbeannamesfortype(object.class));   
      // detecthandlermethodsinancestorcontexts 默认为false,代表不会检测springmvc父容器中的bean的映射关系                                                 for (string beanname : beannames) {                     
//遍历容器中的beanname, 代理的对象跳过,获取当前bean的类型,调用ishandler判断是否是处理器(handler\controller) if (!beanname.startswith(scoped_target_name_prefix)) { class<?> beantype = null; try { beantype = getapplicationcontext().gettype(beanname); } catch (throwable ex) { // an unresolvable bean type, probably from a lazy bean - let's ignore it. if (logger.isdebugenabled()) { logger.debug("could not resolve target class for bean with name '" + beanname + "'", ex); } } if (beantype != null && ishandler(beantype)) { //ishandler方法判断是否是controller,判断逻辑下面有; detecthandlermethods(beanname);  //加载controller和请求映射关系 } } } handlermethodsinitialized(gethandlermethods()); // 该方法是个空实现 }

 

ishandler方法: 判断当前bean的class属性,标注了controller或者requestmapping注解,就会去加载controller和请求映射关系,如果不是handler,迭代下一个bean对象;

    protected boolean ishandler(class<?> beantype) {
        return (annotatedelementutils.hasannotation(beantype, controller.class) ||
                annotatedelementutils.hasannotation(beantype, requestmapping.class));
    }

 

 

detecthandlermethods方法:

protected void detecthandlermethods(final object handler) {
        class<?> handlertype = (handler instanceof string ?
                getapplicationcontext().gettype((string) handler) : handler.getclass());//之前传入handler为string类型,此处去容器获取handler的class
        final class<?> usertype = classutils.getuserclass(handlertype); //处理class为cglib生成class,如果是cglib的获取父类class

        map<method, t> methods = methodintrospector.selectmethods(usertype,
                new methodintrospector.metadatalookup<t>() {
                    @override
                    public t inspect(method method) {
                        return getmappingformethod(method, usertype);  
                    }
                });

        if (logger.isdebugenabled()) {
            logger.debug(methods.size() + " request handler methods found on " + usertype + ": " + methods);
        }
        for (map.entry<method, t> entry : methods.entryset()) {
            method invocablemethod = aoputils.selectinvocablemethod(entry.getkey(), usertype);
            t mapping = entry.getvalue();
            registerhandlermethod(handler, invocablemethod, mapping);
        }
    }

 

 

methodintrospector.selectmethods(..)方法是个很全面的解析方法:注释写得很详细,☆方法处,metadatalookup.inspect方法,往上看,调用的就是getmappingformethod方法获取requestmappinginfo对象;
public static <t> map<method, t> selectmethods(class<?> targettype, final metadatalookup<t> metadatalookup) {
        final map<method, t> methodmap = new linkedhashmap<method, t>();
        set<class<?>> handlertypes = new linkedhashset<class<?>>();
        class<?> specifichandlertype = null;

        if (!proxy.isproxyclass(targettype)) {        
//handler class不是jdk代理生成的,加入到handlertypes集合,specifichandlertype为当前handler class handlertypes.add(targettype); specifichandlertype = targettype; } handlertypes.addall(arrays.aslist(targettype.getinterfaces())); /
/handler class实现的接口加入到handlertypes for (class<?> currenthandlertype : handlertypes) { final class<?> targetclass = (specifichandlertype != null ? specifichandlertype : currenthandlertype); reflectionutils.dowithmethods(currenthandlertype, new reflectionutils.methodcallback() {
//该工具类方法,遍历了该currenthandlertype本类中所有的方法 // 调用的是 getdeclaredmethods(),然后遍历method数组,调用dowith回调处理method方法 public void dowith(method method) { method specificmethod = classutils.getmostspecificmethod(method, targetclass); t result = metadatalookup.inspect(specificmethod);              
// ☆ 核心!!! 这里处理了方法以及类上的映射关系,并且返回泛型t,实际类型是requesmappinginfo if (result != null) { method bridgedmethod = bridgemethodresolver.findbridgedmethod(specificmethod); if (bridgedmethod == specificmethod || metadatalookup.inspect(bridgedmethod) == null) { methodmap.put(specificmethod, result); } } } }, reflectionutils.user_declared_methods);  
// reflectionutils.user_declared_methods是个methodfilter,作用是过滤方法是用户定义、且非桥接类型的方法; } return methodmap; }

 

requestmappinghandlermapping 的 getmappingformethod 方法:先分析方法上的映射关系,再分析类所在方法上的映射关系,然后结合处理;

下面一点点记录我查看这个方法的发现;

protected requestmappinginfo getmappingformethod(method method, class<?> handlertype) {
        requestmappinginfo info = createrequestmappinginfo(method);        
// 解析类上requestmapping注解 if (info != null) { requestmappinginfo typeinfo = createrequestmappinginfo(handlertype);// 解析方法上@requestmapping注解 if (typeinfo != null) { info = typeinfo.combine(info); //方法上requestmapping注解不为空,就需要结合分析 } } return info; }

 

createrequestmappinginfo 方法:

private requestmappinginfo createrequestmappinginfo(annotatedelement element) {
    //调用spring注解工具类annotatedelementutils获取方法上注解 requestmapping requestmapping = annotatedelementutils.findmergedannotation(element, requestmapping.class); requestcondition<?> condition = (element instanceof class<?> ? getcustomtypecondition((class<?>) element) : getcustommethodcondition((method) element)); 
    //requestmappinghandlermapping两个方法都是返回null,空实现 return (requestmapping != null ? createrequestmappinginfo(requestmapping, condition) : null); }

 

具体的requestmappinginfo的构造采用建造者模式还是其他模式的?

protected requestmappinginfo createrequestmappinginfo(
    requestmapping requestmapping, requestcondition<?> customcondition) {  //customcondition一般都为null
  return requestmappinginfo
        .paths(resolveembeddedvaluesinpatterns(requestmapping.path()))// @requestmapping(path={....}) 将path属性设置上去
        .methods(requestmapping.method())     // @requestmapping(method={....}) 将method属性设置上去
        .params(requestmapping.params())        // @requestmapping(method={....}) 将method属性设置上去
        .headers(requestmapping.headers())     // @requestmapping(headers={....}) 将headers属性设置上去
        .consumes(requestmapping.consumes())   // @requestmapping(consumes={....}) 将consumes属性设置上去
        .produces(requestmapping.produces())   // @requestmapping(produces={....}) 将produces属性设置上去
        .mappingname(requestmapping.name())    // @requestmapping(name={....}) 将name属性设置上去   
        .customcondition(customcondition)
        .options(this.config)
         .build();
    }

 

 这里只分析一个开头、一个结尾这样;

requestmappinginfo 的 paths 方法:

    public static builder paths(string... paths) { // paths是@requestmapping的path属性,字符串数组,这里用可变参数来接收,效果一样     
      return new defaultbuilder(paths); }

 

 builder接口所有方法都返回builder对象,defaultbuilder持有一堆属性,可以看到都是@reuqestmapping的属性;

paths方法就将注解的path属性注入到defaultbuilder中,其他方法methods、params、headers、consumes、produces、mappingname、customcondition都是这个套路;

而 options注入的config属性 ,最开始 afterpropertiesset 里 ,this.config = new requestmappinginfo.builderconfiguration();

就是将requestmappinghandlemapping中的config作为defaultbuilder的options注入; 最后就是build方法。

                             SpringMvc流程

defaultbuilder 的 build方法:

    public requestmappinginfo build() {
            contentnegotiationmanager manager = this.options.getcontentnegotiationmanager();
       // patternsrequestcondition构造的主要属性就是paths,代表了映射的路径,不以/开头会添加 / 这个开头
            patternsrequestcondition patternscondition = new patternsrequestcondition(  
                    this.paths, this.options.geturlpathhelper(), this.options.getpathmatcher(),
                    this.options.usesuffixpatternmatch(), this.options.usetrailingslashmatch(),
                    this.options.getfileextensions());                      

            return new requestmappinginfo(this.mappingname, patternscondition,
                    new requestmethodsrequestcondition(methods),    
                    new paramsrequestcondition(this.params),
                    new headersrequestcondition(this.headers),
                    new consumesrequestcondition(this.consumes, this.headers),
                    new producesrequestcondition(this.produces, this.headers, manager),
                    this.customcondition);                                                   // customcondition通常为null
        }

 

build方法返回 requestmappinginfo,其中构造入参都是xxxrequestcondition这种,他们都实现了requestcondition接口;

private patternsrequestcondition(collection<string> patterns, urlpathhelper urlpathhelper,
            pathmatcher pathmatcher, boolean usesuffixpatternmatch, boolean usetrailingslashmatch,
            list<string> fileextensions) {
    //这里就是 prependleadingslash 会判断 @requestmapping注解的 path属性,不是以 /开头会添加 /
        this.patterns = collections.unmodifiableset(prependleadingslash(patterns));   
     this.pathhelper = (urlpathhelper != null ? urlpathhelper : new urlpathhelper()); this.pathmatcher = (pathmatcher != null ? pathmatcher : new antpathmatcher()); this.usesuffixpatternmatch = usesuffixpatternmatch; this.usetrailingslashmatch = usetrailingslashmatch; if (fileextensions != null) { for (string fileextension : fileextensions) { if (fileextension.charat(0) != '.') { fileextension = "." + fileextension; } this.fileextensions.add(fileextension); } } }

 

private static set<string> prependleadingslash(collection<string> patterns) {
        if (patterns == null) {
            return collections.emptyset();
        }
        set<string> result = new linkedhashset<string>(patterns.size());
        for (string pattern : patterns) {
            if (stringutils.haslength(pattern) && !pattern.startswith("/")) {   //url不以 /开头就会自动添加 /
                pattern = "/" + pattern;
            }
            result.add(pattern);
        }
        return result;
    }

 

  回到requestmappinginfo的构造方法,将@requestmapping的所有属性都以 requestcondition的实现类 形式保存到  requestmappinginfo对象中;

接口requestcondition定义了三个方法,1.combine:一般用来 方法级别@requestmapping与类级别@requestmapping结合,返回新的(通常是requestmappinginfo);   

2.getmatchingcondition:检查request对象是否满足条件,返回一个新的满足条件的requestmappinginfo实例(t泛型用都是requestmappinginfo);

 3.compareto  用来多个匹配的情况排序挑选最合适的 

public interface requestcondition<t> {

    t combine(t other);

    t getmatchingcondition(httpservletrequest request);

    int compareto(t other, httpservletrequest request);

}

 

至此 回到 requestmappinghandlermapping 的 getmappingformethod方法 ,第一个方法级别的createrequestmappinginfo方法分析完毕,下面两行解析了标注在 类上的 注解,并且返回 requestmappinginfo对象,

第188行就是类上标注了@requestmapping注解,和方法上同样标注@requestmapping结合处理的步骤:调用类上的requestmappinginfo的combine方法

                 SpringMvc流程

查看requestmappinginfo对象的combine方法:

public requestmappinginfo combine(requestmappinginfo other) {
    // requestmapping的name属性的处理方法,一般name属性很少写,处理方式:两个都不为空就返回this.name#other.name;有一个为空 就返回另外一个name   string name = combinenames(other);  
    //下面逻辑a分析 调用antpathmatcher的combine方法,将类上url和方法上url组合并放入新patternsrequestcondition     patternsrequestcondition patterns = this.patternscondition.combine(other.patternscondition);
    //下面逻辑b分析,并且接下来的methods、params、headers等等实现方式大体一致 requestmethodsrequestcondition methods = this.methodscondition.combine(other.methodscondition); paramsrequestcondition params = this.paramscondition.combine(other.paramscondition); headersrequestcondition headers = this.headerscondition.combine(other.headerscondition);
    //!!comsume和produce判断逻辑不是相加,方法上的该属性优先级高于类级别上的 consumesrequestcondition consumes = this.consumescondition.combine(other.consumescondition); producesrequestcondition produces = this.producescondition.combine(other.producescondition); requestconditionholder custom = this.customconditionholder.combine(other.customconditionholder); return new requestmappinginfo(name, patterns,//返回一个新的requestmappinginfo对象,其中所有requestcondition都是新创建的对象 methods, params, headers, consumes, produces, custom.getcondition()); }

 

逻辑a: patternsrequestcondition 之前介绍过,其属性patterns 就是@requestmapping的path / value 属性的集合,且判断 path是否以  / 开头,如果不是会自动补全  / 开头;

其实现了requestcondition接口,查看其combine方法

    public patternsrequestcondition combine(patternsrequestcondition other) {
          // result作为新的请求路径集合 set<string> result = new linkedhashset<string>();   
          //类上注解@requestmapping path不为空,方法上注解注解@requestmapping path不为空      
            //此处的antpathmatcher就是requestmappinghandlermapping对象里的antpathmatcher对象
            //@requestmapping path属性是集合类型的,这类似笛卡尔积形式 调用antpathmatcher的combine方式,进行url组合 加入到result
    if (!this.patterns.isempty() && !other.patterns.isempty()) { for (string pattern1 : this.patterns) {            
        for (string pattern2 : other.patterns) {         result.add(this.pathmatcher.combine(pattern1, pattern2)); } } }
    //已经说明有一方为空了,只要判断另外一方是否为空,不为空直接加入set<string> else if (!this.patterns.isempty()) {     result.addall(this.patterns); } else if (!other.patterns.isempty()) { result.addall(other.patterns); } else { result.add(""); } /返回了一个新的patternsrequestcondition对象,patterns属性就是当前方法的请求路径 return new patternsrequestcondition(result, this.pathhelper, this.pathmatcher, this.usesuffixpatternmatch, this.usetrailingslashmatch, this.fileextensions); / }

 

 

逻辑a-1:antpathmatcher对象如何对请求路径进行结合combine? 

  类上path  方法上path 结合后path
null null  
/hotels null /hotels
null /hotels /hotels
/hotels /bookings /hotels/bookings
/hotels bookings /hotels/bookings
/hotels/* /bookings /hotels/bookings
/hotels/** /bookings /hotels/**/bookings
/hotels {hotel} /hotels/{hotel}
/hotels/* {hotel} /hotels/{hotel}
/hotels/** {hotel} /hotels/**/{hotel}
/*.html hotels.html /hotels.html
/*.html /hotels /hotels.html
/*.html /*.txt illegalargumentexception

 

 逻辑b:requestmethodsrequestcondition 的 combine 方法,方法上注解@requestmapping的method加入到类上注解的method属性里,然后返回一个全新的requestmethodsrequestcondition,持有新的method集合;

public requestmethodsrequestcondition combine(requestmethodsrequestcondition other) {
        set<requestmethod> set = new linkedhashset<requestmethod>(this.methods);
        set.addall(other.methods);
        return new requestmethodsrequestcondition(set);
    }

 

 getmappingformethod方法调用结束,返回结合后的requestmappinginfo对象; 回到methodintrospector.selectmethods方法,第19行就是调用的getmappingformethod方法,返回requestmappinginfo对象result,result不为空之后,

会筛选不是桥接方法,存入methodmap这个map对象,key-type是method,value-type是requestmappinginfo类型;

该方法selectmethods将controller / handler中所有方法都进行判断加载请求映射,返回methodmap对象;

 1     public static <t> map<method, t> selectmethods(class<?> targettype, final metadatalookup<t> metadatalookup) {
 2         final map<method, t> methodmap = new linkedhashmap<method, t>();
 3         set<class<?>> handlertypes = new linkedhashset<class<?>>();
 4         class<?> specifichandlertype = null;
 5 
 6         if (!proxy.isproxyclass(targettype)) {
 7             handlertypes.add(targettype);
 8             specifichandlertype = targettype;
 9         }
10         handlertypes.addall(arrays.aslist(targettype.getinterfaces()));
11 
12         for (class<?> currenthandlertype : handlertypes) {
13             final class<?> targetclass = (specifichandlertype != null ? specifichandlertype : currenthandlertype);
14 
15             reflectionutils.dowithmethods(currenthandlertype, new reflectionutils.methodcallback() {
16                 @override
17                 public void dowith(method method) {
18                     method specificmethod = classutils.getmostspecificmethod(method, targetclass);
19                     t result = metadatalookup.inspect(specificmethod);
20                     if (result != null) {
21                         method bridgedmethod = bridgemethodresolver.findbridgedmethod(specificmethod);
22                         if (bridgedmethod == specificmethod || metadatalookup.inspect(bridgedmethod) == null) {
23                             methodmap.put(specificmethod, result);
24                         }
25                     }
26                 }
27             }, reflectionutils.user_declared_methods);
28         }
29 
30         return methodmap;
31     }

 

回到最开始的分析detecthandlermethods方法:methods对象就是上面返回的methodmap,如果日志设置了debug,每遍历一个controller都会输出日志;

 SpringMvc流程

 1 protected void detecthandlermethods(final object handler) {
 2         class<?> handlertype = (handler instanceof string ?
 3                 getapplicationcontext().gettype((string) handler) : handler.getclass());
 4         final class<?> usertype = classutils.getuserclass(handlertype);
 5 
 6         map<method, t> methods = methodintrospector.selectmethods(usertype,
 7                 new methodintrospector.metadatalookup<t>() {
 8                     @override
 9                     public t inspect(method method) {
10                         return getmappingformethod(method, usertype);
11                     }
12                 });
13 
14         if (logger.isdebugenabled()) {
15             logger.debug(methods.size() + " request handler methods found on " + usertype + ": " + methods);
16         }
17         for (map.entry<method, t> entry : methods.entryset()) {//遍历methods,并且调用registerhandlermethod注册映射信息
18             method invocablemethod = aoputils.selectinvocablemethod(entry.getkey(), usertype);
19             t mapping = entry.getvalue();
20             registerhandlermethod(handler, invocablemethod, mapping);
21         }
22     }

 

registerhandlermethod:总结 :

 

requestmappinghandlermapping.mappingregistry属性

 

  key-type value-type
mappinglookup
requestmappinginfo handlermethod对象
urllookup
请求路径url requestmappinginfo
namelookup controller name中大写字母#方法名(如uc#test) handlermethod对象
registry
requestmappinginfo
mappingregistration对象(持有
requestmappinginfo\handlermethod\url路径\name)

 

protected void registerhandlermethod(object handler, method method, t mapping) {
        this.mappingregistry.register(mapping, handler, method);   
} // this对象指requestmappinghandlermapping,mapping是requestmappinginfo对象,handler是controler的name,method是当前@requestmapping方法

public void register(t mapping, object handler, method method) {
            this.readwritelock.writelock().lock();   //可重入锁 写锁上锁  这里不太明白为什么要上锁
            try {
          //创建新的handlermethod对象 下面逻辑c 介绍handlermethod 逻辑d 分析createhandlermethod方法 handlermethod handlermethod = createhandlermethod(handler, method);
          //校验唯一性,一个requestmappinginfo对应一个handlermethod,如果根据requestmappinginfo找到不同的hm 抛出异常 assertuniquemethodmapping(handlermethod, mapping); //info级别日志 比如mapped "{[/user/test]}" onto public java.lang.string demo2.usercontroller.test(javax.servlet.http.httpservletrequest,javax.servlet.http.httpservletresponse) if (logger.isinfoenabled()) { logger.info("mapped \"" + mapping + "\" onto " + handlermethod); }
           //this指requestmappinghandlermapping.mappingregistry,mappinglookup保存着requestmappinginfo--handlermethod对象 this.mappinglookup.put(mapping, handlermethod);            //获取 mapping 的patternsrequestcondition的patterns,也就是拼接的url路径,并且路径不包含* ?的就加入到集合返回 , list<string> directurls = getdirecturls(mapping);
          for (string url : directurls) {
            //mappingregistry的urllookup保存着 url--requestmappinginfo对象 this.urllookup.add(url, mapping);   } string name = null; if (getnamingstrategy() != null) {
             //name属性感觉没用,如果@requestmapping有name属性就是这个属性 如果没有就是 controller名字中的大写字母#方法名字,比如uc#test name = getnamingstrategy().getname(handlermethod, mapping);
              //mappingregistry的namelookup保存着 name--handlermethod集合 addmappingname(name, handlermethod); } corsconfiguration corsconfig = initcorsconfiguration(handler, method, mapping); if (corsconfig != null) { this.corslookup.put(handlermethod, corsconfig); }
     //mappingregistry的registry保存着requestmappinginfo--mappingregistration,mappingregistration几乎有映射的所有信息 this.registry.put(mapping, new mappingregistration<t>(mapping, handlermethod, directurls, name)); } finally { this.readwritelock.writelock().unlock(); //可重入锁 写锁 释放锁 } }

 

逻辑c:handlermethod对象 属性有bean,就是controller对象实例;beanfactory当前spring容器;beantype就是controller的类型;method就是handler method;birdgemethod是handler method的桥接方法;methodparameter是handler method的方法参数,handlermethod一般为null;

  handlermethod,作用spring给出了:一个handler method对象,包含了method以及controller对象,此外提供了便捷方式获取方法入参、返回值、注解等等;

           SpringMvc流程

 

 逻辑d:createhandlermethod方法只是调用了handlermethod的构造方法,构造方法中对方法入参进行了处理;

 1     protected handlermethod createhandlermethod(object handler, method method) {
 2         handlermethod handlermethod;
 3         if (handler instanceof string) {
 4             string beanname = (string) handler;
 5             handlermethod = new handlermethod(beanname,
 6                     getapplicationcontext().getautowirecapablebeanfactory(), method);
 7         }
 8         else {
 9             handlermethod = new handlermethod(handler, method);
10         }
11         return handlermethod;
12     }
13 
14 public handlermethod(string beanname, beanfactory beanfactory, method method) {
15         assert.hastext(beanname, "bean name is required");
16         assert.notnull(beanfactory, "beanfactory is required");
17         assert.notnull(method, "method is required");
18         this.bean = beanname;            //controller beanname
19         this.beanfactory = beanfactory;      //当前controller所在spring工厂
20         this.beantype = classutils.getuserclass(beanfactory.gettype(beanname));   //获取当前controller类型
21         this.method = method;            //当前handler method
22         this.bridgedmethod = bridgemethodresolver.findbridgedmethod(method);  //查找method的桥接方法,没有桥接方法就是返回自身
23         this.parameters = initmethodparameters();   //初始化methodparameter对象 设置了每个methodparameter的method、parameterindex属性 具体方法下图
24         this.resolvedfromhandlermethod = null;
25     }

                                         SpringMvc流程

至此,registerhandlermethod方法分析完毕,detecthandlermethods方法分析完成,

spring主要做了哪些工作:将所有请求映射关系保存到上面requestmappinghandlermapping的mappingregistry的相关属性中,详情见上面表格。

 

分析过springmvc的请求流程  springmvc流程

  篇幅太长,只分析如何找根据请求找到对应的handler?  遍历handlermapping对象,调用其gethanlder方法查找controller / handler , requestmappinghandlermapping对象的父类abstracthandlermapping实现了gethandler方法,方法最开始object handler = gethandlerinternal(request); 那么我们从abstracthandlermapping 的 gethandlerinternal开始记录.

 1 protected handlermethod gethandlerinternal(httpservletrequest request) throws exception {
       // 根据request请求路径以及servlet-mapping得到要请求url 2 string lookuppath = geturlpathhelper().getlookuppathforrequest(request); 3 if (logger.isdebugenabled()) { 4 logger.debug("looking up handler method for path " + lookuppath); 5 } 6 this.mappingregistry.acquirereadlock();                        //读锁 上锁    7 try {
           // 这里就是mvc寻找controller匹配的方法! 下面花大篇幅介绍下 8 handlermethod handlermethod = lookuphandlermethod(lookuppath, request);
9 if (logger.isdebugenabled()) { 10 if (handlermethod != null) { 11 logger.debug("returning handler method [" + handlermethod + "]"); 12 } 13 else { 14 logger.debug("did not find handler method for [" + lookuppath + "]"); 15 } 16 }
          //找到handlermethod,但bean是controller beanname,用beanfactory getbean替换bean 17 return (handlermethod != null ? handlermethod.createwithresolvedbean() : null); 18 } 19 finally { 20 this.mappingregistry.releasereadlock(); 21 } 22 }

 lookuphandlermethod方法:

  逻辑是这样的,先根据请求的url 从 requestmappinghandlermapping的mappingregistryurllookup中尝试寻找requestmappinginfo;

  寻找大致分为两种情况:一种请求url清楚,不需要通配符比对,那肯定可以直接找到requestmappinginfo集合,创建match对象并且添加到集合里面,然后根据规则对match集合排序选出最优解;

     第二种情况url带有通配符,那需要遍历映射关系再重复第一种情况。

 1 protected handlermethod lookuphandlermethod(string lookuppath, httpservletrequest request) throws exception {
 2         list<match> matches = new arraylist<match>();
        //return this.urllookup.get(urlpath);调用mappingregistry的urllookup根据url寻找requestmappinginfo 3      list<t> directpathmatches = this.mappingregistry.getmappingsbyurl(lookuppath);
4 if (directpathmatches != null) {     
          //遍历找到的requestmappinginfo集合, 然后寻找匹配的对象并处理添加到matches集合,见 逻辑e 分析     5 addmatchingmappings(directpathmatches, matches, request);
6 } 7 if (matches.isempty()) {   //matches为空,有可能是因为通配符匹配的情况需要再次匹配 8 // no choice but to go through all mappings... 9 addmatchingmappings(this.mappingregistry.getmappings().keyset(), matches, request); 10 } 11 12 if (!matches.isempty()) {                                     
          //返回一个matchcomparator对象
          // 持有comparator属性,并且compare方法是调用了requestmappinginfo的compareto 13 comparator<match> comparator = new matchcomparator(getmappingcomparator(request));         
           //说到底排序还是调用了requestmappinginfo的compareto方法, 也存在优先级之分 url路径>params>headers>comsume>produce>method 排序分析见文章最后 14 collections.sort(matches, comparator); 15 if (logger.istraceenabled()) { 16 logger.trace("found " + matches.size() + " matching mapping(s) for [" + 17 lookuppath + "] : " + matches); 18 } 19 match bestmatch = matches.get(0); //找到最优匹配 20 if (matches.size() > 1) { 21 if (corsutils.ispreflightrequest(request)) { 22 return preflight_ambiguous_match; 23 } 24 match secondbestmatch = matches.get(1); 25 if (comparator.compare(bestmatch, secondbestmatch) == 0) { //存在两个匹配且相等 抛出异常 26 method m1 = bestmatch.handlermethod.getmethod(); 27 method m2 = secondbestmatch.handlermethod.getmethod(); 28 throw new illegalstateexception("ambiguous handler methods mapped for http path '" + 29 request.getrequesturl() + "': {" + m1 + ", " + m2 + "}"); 30 } 31 } 32 handlematch(bestmatch.mapping, lookuppath, request); //解析url变量,完成设置request属性等工作 33 return bestmatch.handlermethod;               //返回最优匹配的handlermethod对象 34 } 35 else { //没找到handlermethod 就返回null 36 return handlenomatch(this.mappingregistry.getmappings().keyset(), lookuppath, request); 37 } 38 }

 

逻辑e:遍历找到的requestmappinginfo集合,调用requestmappinginfo的getmatchcondition进行匹配以获取匹配的requestmappinginfo对象;寻找到合适的requestmappinginfo对象之后,创建一个match对象加入matches集合;

mappingregistry 的 getmappings方法返回mappinglookup属性,上述表格mappinglookup 存放 requestmappinginfo--handlermethod,根据requestmappinginfo对象从map中取对象,(逻辑g分析 requestmappinginfo重写了的hashcode以及equals方法)。 match对象持有requestmappinginfo以及handlermethod属性;此处方法调用结束matches可能包含多个match结果;

            SpringMvc流程

requestmappinginfo的getmatchingcondition方法

 1 public requestmappinginfo getmatchingcondition(httpservletrequest request) {
  //如果requestmappinginfo没有指定methods属性,返回requestmappinginfo本身,否则方法匹配 2 requestmethodsrequestcondition methods = this.methodscondition.getmatchingcondition(request);
        //下面几个匹配逻辑是一样的,匹配了返回自身,没匹配返回null,具体参数作用、如何匹配看吧; 3 paramsrequestcondition params = this.paramscondition.getmatchingcondition(request); 4 headersrequestcondition headers = this.headerscondition.getmatchingcondition(request); 5 consumesrequestcondition consumes = this.consumescondition.getmatchingcondition(request); 6 producesrequestcondition produces = this.producescondition.getmatchingcondition(request); 7         //有一个条件匹配不上就直接返回null 8 if (methods == null || params == null || headers == null || consumes == null || produces == null) {
9 return null; 10 } 11         //其他匹配上了,最重要的匹配请求url, 路径匹配作为 逻辑f 分析 12 patternsrequestcondition patterns = this.patternscondition.getmatchingcondition(request);
13 if (patterns == null) { 14 return null; 15 } 16 17 requestconditionholder custom = this.customconditionholder.getmatchingcondition(request); 18 if (custom == null) { 19 return null; 20 } 21 22 return new requestmappinginfo(this.name, patterns,
             //寻找到匹配之后,构造一个新的requestmappinginfo对象,持有上述匹配之后的结果返回 23 methods, params, headers, consumes, produces, custom.getcondition()); 24 }

 

 SpringMvc流程

逻辑f:patternsrequestcondition匹配

调用patternsrequestcondition 的 getmatchingpattern 方法进行url匹配;遍历patternsrequestcondition的 patterns属性,逐个getmatchingpattern进行比较,匹配上将pattern存入集合,并且使用antpatterncomparator进行排序,排序之后集合加入到一个新的patternsrequestcondition对象中;

  //pattern就是patterns属性当前迭代的元素,lookuppath就是servlet-mapping下请求url
1 private string getmatchingpattern(string pattern, string lookuppath) { 2 if (pattern.equals(lookuppath)) {   //两者相等 无疑义直接返回   这种是没有通配符 * ?这种都会很容易匹配到并且返回       3 return pattern; 4 } 5 if (this.usesuffixpatternmatch) { // usesuffixpatternmatch默认为true 6 if (!this.fileextensions.isempty() && lookuppath.indexof('.') != -1) { // fileextensions默认为空 7 for (string extension : this.fileextensions) { 8 if (this.pathmatcher.match(pattern + extension, lookuppath)) { 9 return pattern + extension; 10 } 11 } 12 } 13 else { 14 boolean hassuffix = pattern.indexof('.') != -1; //pattern字符串是否有 . 15 if (!hassuffix && this.pathmatcher.match(pattern + ".*", lookuppath)) { //没有 . 就用antpathmatcher的match匹配 pattern.* lookuppath 16 return pattern + ".*"; 17 } 18 } 19 } 20 if (this.pathmatcher.match(pattern, lookuppath)) {
      // 用antpathmatcher的match匹配 pattern lookuppath,匹配上就返回pattern 21 return pattern; 22 } 23 if (this.usetrailingslashmatch) { 24 if (!pattern.endswith("/") && this.pathmatcher.match(pattern + "/", lookuppath)) { 25 return pattern +"/"; 26 } 27 } 28 return null; 29 }

 

逻辑g:先介绍下为什么要看requestmappinginfo的hashcode以及equals方法?requestmappinginfo作为key存储在map中,肯定需要重写hashcode以及equals方法;

    requestmappinginfo的hashcode以及equals方法:  比较的时候会先调用hashcode判断值是否相等,相等再比较equals方法,如果相等则认为是同一个对象;

    先来看hashcode方法,将requestmappinginfo的所有requestcondition属性按公式求和,这些属性都是abstractrequestcondition,equals和hashcode方法都调用了getcontent方法,而abstractrequestcondition的各种实现类的getcontent方法,比如patternsrequestcondition实现方式就是返回patterns(url)集合;比如requestmethodsrequestcondition实现就是返回methods集合;

 requestmappinginfo
1 public boolean equals(object other) { 2 if (this == other) { 3 return true; 4 } 5 if (!(other instanceof requestmappinginfo)) { 6 return false; 7 } 8 requestmappinginfo otherinfo = (requestmappinginfo) other; 9 return (this.patternscondition.equals(otherinfo.patternscondition) && 10 this.methodscondition.equals(otherinfo.methodscondition) && 11 this.paramscondition.equals(otherinfo.paramscondition) && 12 this.headerscondition.equals(otherinfo.headerscondition) && 13 this.consumescondition.equals(otherinfo.consumescondition) && 14 this.producescondition.equals(otherinfo.producescondition) && 15 this.customconditionholder.equals(otherinfo.customconditionholder)); 16 } 17 18 @override 19 public int hashcode() { 20 return (this.patternscondition.hashcode() * 31 + // primary differentiation 21 this.methodscondition.hashcode() + this.paramscondition.hashcode() + 22 this.headerscondition.hashcode() + this.consumescondition.hashcode() + 23 this.producescondition.hashcode() + this.customconditionholder.hashcode()); 24 }

 

abstractrequestcondition  
1 public boolean equals(object obj) { 2 if (this == obj) { 3 return true; 4 } 5 if (obj != null && getclass() == obj.getclass()) { 6 abstractrequestcondition<?> other = (abstractrequestcondition<?>) obj; 7 return getcontent().equals(other.getcontent()); 8 } 9 return false; 10 } 11 12 @override 13 public int hashcode() { 14 return getcontent().hashcode(); 15 }

 

 

 分析到上面,gethandlerinternal已经找到了对应的handlermethod对象,调用gethandlerexecutionchain封装成handlerexecutionchain;

 1 protected handlerexecutionchain gethandlerexecutionchain(object handler, httpservletrequest request) {
 2         handlerexecutionchain chain = (handler instanceof handlerexecutionchain ?
 3                 (handlerexecutionchain) handler : new handlerexecutionchain(handler)); 
           // //构造一个handlerexecutionchain对象,持有handlermethod 4 5 string lookuppath = this.urlpathhelper.getlookuppathforrequest(request); 6 for (handlerinterceptor interceptor : this.adaptedinterceptors) {
      //adaptedinterceptors在开启<mvc:annotation-drvien/>之后不为空,多了一个mappedinterceptor拦截器 7 if (interceptor instanceof mappedinterceptor) { 8 mappedinterceptor mappedinterceptor = (mappedinterceptor) interceptor; 9 if (mappedinterceptor.matches(lookuppath, this.pathmatcher)) { 10 chain.addinterceptor(mappedinterceptor.getinterceptor());     
      //将conversionserviceexposinginterceptor添加到handlerexecutionchain的interceptorlist属性中 11 } 12 } 13 else { 14 chain.addinterceptor(interceptor); 15 } 16 } 17 return chain; //返回handlerexecutionchain对象 18 }

 

tip1:这个requestmappinghandlermapping的mappedinterceptor是从哪里注入的呢?

开启了<mvc:annotation-driven />之后 spring向容器中注入了这样两个bean的定义,mappedinterceptor,该对象持有conversionserviceexposinginterceptor对象;

        SpringMvc流程

容器中有了mappedinterceptor对象,什么时候给requestmappinghandlermapping设置的adaptedinterceptors呢?通过打断点分析到,requestmappinghandlermapping实现了applicationcontextaware接口,spring向其注入applicationcontext的时候,调用了initapplicationcontext方法,不断进入方法最后进入到父类abstracthandlermapping的initapplicationcontext方法,

 1 protected void initapplicationcontext() throws beansexception {
 2         extendinterceptors(this.interceptors);
 3         detectmappedinterceptors(this.adaptedinterceptors);   //此处添加了requestmappinghandlermapping的adaptedinterceptors
 4         initinterceptors();
 5 }
 6 
 7 protected void detectmappedinterceptors(list<handlerinterceptor> mappedinterceptors) {
       //这里将容器中的mappedinterceptor添加到了requestmappinghandlermapping的adaptedinterceptors 8 mappedinterceptors.addall(                   
9 beanfactoryutils.beansoftypeincludingancestors( 10 getapplicationcontext(), mappedinterceptor.class, true, false).values()); 11 }

 

至此,如何找到handlermethod已经分析完毕;

 

总结

springmvc请求寻找规则 : 如果一个请求同时匹配上多个方法,按照如下顺序选择执行哪个方法:

先url匹配的方法 >>>>>  params满足的方法  >>>>>  headers 满足的方法  >>>>>>consume满足的方法 >>>> produce 满足的方法 >>>> method满足的方法

 

如果一个请求匹配上了多个requestmappinginfo筛选:

之前介绍过排序是调用 requestmappinginfo的compareto进行排序

 1 public int compareto(requestmappinginfo other, httpservletrequest request) {
 2         int result = this.patternscondition.compareto(other.getpatternscondition(), request);  //优先url进行匹配
 3         if (result != 0) {
 4             return result;
 5         }
 6         result = this.paramscondition.compareto(other.getparamscondition(), request);
 7         if (result != 0) {
 8             return result;
 9         }
10         result = this.headerscondition.compareto(other.getheaderscondition(), request);
11         if (result != 0) {
12             return result;
13         }
14         result = this.consumescondition.compareto(other.getconsumescondition(), request);
15         if (result != 0) {
16             return result;
17         }
18         result = this.producescondition.compareto(other.getproducescondition(), request);
19         if (result != 0) {
20             return result;
21         }
22         result = this.methodscondition.compareto(other.getmethodscondition(), request);
23         if (result != 0) {
24             return result;
25         }
26         result = this.customconditionholder.compareto(other.customconditionholder, request);
27         if (result != 0) {
28             return result;
29         }
30         return 0;
31     }

 

 介绍下url如何排序吧,其他类似; 假设两个url   /get1  可以被匹配  /get*  以及 /get?

 1 public int compareto(patternsrequestcondition other, httpservletrequest request) {
 2         string lookuppath = this.pathhelper.getlookuppathforrequest(request);
 3         comparator<string> patterncomparator = this.pathmatcher.getpatterncomparator(lookuppath);  //获取antpatterncomparator比较器
 4         iterator<string> iterator = this.patterns.iterator();
 5         iterator<string> iteratorother = other.patterns.iterator();
 6         while (iterator.hasnext() && iteratorother.hasnext()) {
 7             int result = patterncomparator.compare(iterator.next(), iteratorother.next());   //url比较规则在这里
 8             if (result != 0) {
 9                 return result;
10             }
11         }
12         if (iterator.hasnext()) {
13             return -1;
14         }
15         else if (iteratorother.hasnext()) {
16             return 1;
17         }
18         else {
19             return 0;
20         }
21     }

 

 url比较规则:按照请求url通配符按一定权重计算排序顺序,{个数+*个数+ ** 个数 ;所以 get* 比get?排在前面;

 1 public int compare(string pattern1, string pattern2) {      //例子中pattern1为 /get*  pattern2为/get?
 2             patterninfo info1 = new patterninfo(pattern1);  //具体查看下面构造方法
 3             patterninfo info2 = new patterninfo(pattern2);
 4 
 5             if (info1.isleastspecific() && info2.isleastspecific()) {
 6                 return 0;
 7             }
 8             else if (info1.isleastspecific()) {
 9                 return 1;
10             }
11             else if (info2.isleastspecific()) {                     //上面三种情况是 比较 /**的情况
12                 return -1;
13             }
14 
15             boolean pattern1equalspath = pattern1.equals(path);     
16             boolean pattern2equalspath = pattern2.equals(path);
17             if (pattern1equalspath && pattern2equalspath) {
18                 return 0;
19             }
20             else if (pattern1equalspath) {
21                 return -1;
22             }
23             else if (pattern2equalspath) {                        //这三种情况是比较  pattern1 pattern2存在和请求url完全匹配的情况
24                 return 1;
25             }
26 
27             if (info1.isprefixpattern() && info2.getdoublewildcards() == 0) {
28                 return 1;
29             }
30             else if (info2.isprefixpattern() && info1.getdoublewildcards() == 0) {   //哪个pattern的 /**多 哪个排在前面
31                 return -1;
32             }
33 
34             if (info1.gettotalcount() != info2.gettotalcount()) {
35                 return info1.gettotalcount() - info2.gettotalcount();   //按照权重来排序了   {算1   *算1 **算2  哪个大哪个排前面  /get*权重为1排前面
36             }
37 
38             if (info1.getlength() != info2.getlength()) {
39                 return info2.getlength() - info1.getlength();
40             }
41 
42             if (info1.getsinglewildcards() < info2.getsinglewildcards()) {
43                 return -1;
44             }
45             else if (info2.getsinglewildcards() < info1.getsinglewildcards()) {
46                 return 1;
47             }
48 
49             if (info1.geturivars() < info2.geturivars()) {
50                 return -1;
51             }
52             else if (info2.geturivars() < info1.geturivars()) {
53                 return 1;
54             }
55 
56             return 0;
57         }
58 
59 public patterninfo(string pattern) {
60                 this.pattern = pattern;
61                 if (this.pattern != null) {
62                     initcounters();      
63                     this.catchallpattern = this.pattern.equals("/**");      //代表匹配所有就是pattern为 /**
64                     this.prefixpattern = !this.catchallpattern && this.pattern.endswith("/**");
65                 }
66                 if (this.urivars == 0) {
67                     this.length = (this.pattern != null ? this.pattern.length() : 0);
68                 }
69             }
70 
71             protected void initcounters() {
72                 int pos = 0;
73                 while (pos < this.pattern.length()) {
74                     if (this.pattern.charat(pos) == '{') {   //存在变量 则urivars自增
75                         this.urivars++;
76                         pos++;
77                     }
78                     else if (this.pattern.charat(pos) == '*') {          //解析到*
79                         if (pos + 1 < this.pattern.length() && this.pattern.charat(pos + 1) == '*') {
80                             this.doublewildcards++;      // doublewildcards代表有两个*的
81                             pos += 2;
82                         }
83                         else if (pos > 0 && !this.pattern.substring(pos - 1).equals(".*")) {    //最后一位是* 且倒数第二位不是 *    
84                             this.singlewildcards++;     // singlewildcards代表有单个*
85                             pos++;
86                         }
87                         else {
88                             pos++;
89                         }
90                     }
91                     else {
92                         pos++;
93                     }
94                 }
95             }