在SpringMVC中,如果我们需要开启注解,即:我们经常使用的@RequestMapping,@Controller注解需要生效,通常使用的是提供的标签SpringMVC框架“”来完成。那么这个标签是如何完成注解驱动使注解生效的呢?让我们揭示有效的过程!1、标签有什么作用?首先要知道标签是干什么的,首先要搞清楚标签是怎么解析的。关于标签解析,在之前的文章中有详细的介绍,可以参考文章:《Spring标签生效可真不容易》,这里我们直接找到对应的标签解析器AnnotationDrivenBeanDefinitionParser类,然后在类中找到parse方法,核心操作是通过new方法创建了几个BeanDefinitions并放置在容器中。其中有一个比较重要的Definition,叫做RequestMappingHandlerMapping。当然还有其他几个BeanDefinition,不用着急,我们后面会介绍。总结:这里的tag暂时可以认为是在做一件事情,就是创建一个RequestMappingHandlerMapping类型的bean定义,然后放到Spring的bean定义注册中心。2、需要做哪些准备工作?上面的第1步完成后,容器中就会多出一个RequestMappingHandlerMapping类型的bean定义,那么下一步就必须通过这个bean定义来创建bean了。如何制作?先看RequestMappingHandlerMapping类的继承关系图,如下:这张看似复杂的继承关系图其实很简单,只是实现了Spring提供的一堆扩展点接口。大家可以看看Aware最后的扩展点,里面没有什么特别的操作。查看RequestMappingHandlerMapping中InitializaingBean扩展点方法的实现。点击后代码如下:@OverridepublicvoidafterPropertiesSet(){this.config=newRequestMappingInfo.BuilderConfiguration();this.config.setUrlPathHelper(getUrlPathHelper());this.config.setPathMatcher(getPathMatcher());this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);this.config.setMantenterNegotiation(getContentNegotiationManager());super.afterPropertiesSet();}看似和@RequestMapping无关,但是调用父类的afterPropertiesSet方法后,打开父类中的实现后,会发现一些小技巧,代码如下:@OverridepublicvoidafterPropertiesSet(){initHandlerMethods();}根据Spring框架中的标准命名约定,可以猜到这是初始化处理器方法。继续查看这个方法,可以看到如下代码,根据Bean的名字做了一个过滤。我们在上一篇文章中介绍过,在Spring中声明的bean的名字可以指定,也可以不指定。如果不指定,默认根据类的全限定名生成,不能以"scopedTarget"开头。所以会调用processCandicateBean方法!protectedvoidinitHandlerMethods(){for(StringbeanName:getCandidateBeanNames()){//如果bean的名称不以“scopedTarget”开头。if(!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)){//处理CandidateBeanprocessCandidateBean(beanName);}}handlerMethodsInitialized(getHandlerMethods());}在processCandidateBean方法中,首先根据bean名称调用BeanFactory提供的getType方法获取Bean定义的类类型,然后进行非常关键的判断,代码如下如下:protectedvoidprocessCandidateBean(StringbeanName){Class>beanType=null;尝试{beanType=obtainApplicationContext().getType(beanName);}catch(Throwableex){}//如果根据bean名称解析出来的beanType是带有@Controler或者@RequestMapping注解的,那么分析请求映射方法。if(beanType!=null&&isHandler(beanType)){//分析请求URL与Controller的映射关系detectHandlerMethods(beanName);}}这个if判断中的isHandler方法实现如下:@OverrideprotectedbooleanisHandler(Class>beanType){return(AnnotatedElementUtils.hasAnnotation(beanType,Controller.class)||AnnotatedElementUtils.hasAnnotation(beanType,RequestMapping.class));}可以看到,就是判断class是否打上了@Controller注解和@RequestMapping注解。因此,可以加锁,processCandidateBean这个方法用来完成@RequestMapping注解功能的准备。如果类上标记了这两个注解中的任何一个,就会执行detectHandlerMethods方法,即检测handler方法。不得不佩服写Spring框架的大神们,命名的这么形象贴切!完成后继续查看detectHandlerMethods方法,如下:protectedvoiddetectHandlerMethods(Objecthandler){//获取Handler的类类型,即Controller对应的类Class>handlerType=(handlerinstanceofString?obtainApplicationContext().getType((String)handler):handler.getClass());if(handlerType!=null){//通过类名是否包含$$符号判断当前Class是否为代理类Class>userType=ClassUtils.getUserClass(handlerType);Mapmethods=MethodIntrospector.selectMethods(userType,(MethodIntrospector.MetadataLookup)方法->{try{returngetMappingForMethod(method,userType);}catch(Throwableex){}});//mapping是一个RequestMappingInfo对象methods.forEach((method,mapping)->{MethodinvocableMethod=AopUtils.selectInvocableMethod(method,userType);registerHandlerMethod(handler,invocableMethod,mapping);});}}这个方法看起来写的比较霸道,但是你只需要确认三点:(1)返回的方法是一个Map类型,key是一个方法对象,value是什么?视图M实现了methodIntrospector的selectMethods方法。这个值其实就是这个方法的第二个回调函数的返回结果,也就是getMappingForMethod的结果。检查此getMappingForMethod方法。它的实现在RequestMappingHandlerMapping中。代码如下:@Override@NullableprotectedRequestMappingInfogetMappingForMethod(Methodmethod,Class>handlerType){//将方法上的@RequestMapping注解解析成一个RequestMappingInfo对象RequestMappingInfoinfo=createRequestMappingInfo(method);if(info!=null){//将类上的@RequestMapping注解解析为RequestMappingInfo对象RequestMappingInfotypeInfo=createRequestMappingInfo(handlerType);//如果类上有注解,则将类上的@RequestMapping和方法上的@RequestMapping中的信息合并,例如:mergerequestUriif(typeInfo!=null){info=typeInfo.combine(info);}Stringprefix=getPathPrefix(handlerType);if(prefix!=null){info=RequestMappingInfo.paths(prefix).build().combine(info);}}returninfo;}可以看出该方法的返回值类型是RequestMappingInfo类型。根据这个方法的名字和实现逻辑可以看出,这个对象是用来封装方法和处理器的映射关系的。(2)getMappingForMethod如何解析HandlerMethod?它将类上的@RequestMapping注解信息和方法上的@RequestMapping信息合并,通常是我们的请求uri合并。即:“类上有一个@RequestMapping("/api/user"),listUser方法上有一个@RequestMapping("/listUser"),通过typeInfo.combine后,就变成了/api/user/listUser再次夸一下Spring的方法命名,太贴切了。上面代码在创建RequestMapping时,底层使用了builder设计模式,代码看起来特别优雅,如下:生成器生成器=RequestMappingInfo.paths(resolveEmbeddedValuesInPatterns(requestMapping.path())).methods(requestMapping.method()).params(requestMapping.params()).headers(requestMapping.headers()).consumes(requestMapping.consumes())。生产(requestMapping.produces()).mappingName(requestMapping.name());if(customCondition!=null){builder.customCondition(customCondition);}returnbuilder.options(this.config).build();}so就算看不懂代码,学习一下大佬们的编码习惯也不错!(3)拿到方法后,循环中的registerHandlerMethod在哪里注册这个方法?打开registerHandlerMethod方法后发现注册handler方法是通过一个mapper注册中心MappingRegistry完成的,代码如下:,handler,method);}查看这个MappingRegistry的register方法,可以看出保存@RequestMapping请求路径和handler方法的真正逻辑,如下:publicvoidregister(Tmapping,Objecthandler,Methodmethod){//因为是在Spring启动的时候注册的,可能会有在各个虚拟机上重复注册的可能,保证线程安全this.readWriteLock.writeLock().lock();try{//创建HandlerMethod对象HandlerMethodhandlerMethod=createHandlerMethod(handler,method);//验证HandlerMethod的唯一性assertUniqueMethodMapping(handlerMethod,mapping);//注册RequestHandlerMappingInfo和HandlerMethod的对应关系this.mappingLookup.put(mapping,handlerMethod);//查找具有匹配字符的URLurlListdirectUrls=getDirectUrls(mapping);for(Stringurl:directUrls){this.urlLookup.add(url,mapping);}字符串名称=空;//如果设置了Mapping名称的生成策略,则使用Strategy生成Mapping名称//创建RequestMappingHandlerMapping时设置的名称生成策略为:RequestMappingInfoHandlerMethodMappingNamingStrategyif(getNamingStrategy()!=null){name=getNamingStrategy().getName(handlerMethod,映射);addMappingName(名字,handlerM方法);}//初始化跨域配置CorsConfigurationcorsConfig=initCorsConfiguration(handler,method,mapping);如果(corsConfig!=null){this.corsLookup.put(handlerMethod,corsConfig);}this.registry.put(mapping,newMappingRegistration<>(mapping,handlerMethod,directUrls,name));}最后{this.readWriteLock.writeLock().unlock();}}这个方法还有两个重点:①首先有一个assertUniqueMethodMapping方法,这个方法用来验证RequestMapping是否唯一。有时多个Controller中@RequestMapping中的路径重复,以此来验证启动错误;②SpringMVC的请求路径与处理器的映射关系,而不是url和method直接的映射关系,而是保存了几套映射关系,包括:a.mapping和HandlerMethod;b.url和映射;c.mapping和MappingRegistration。这个MappingRegistration其实是在HandlerMethodd.HandlerMethod和corsConfig中,跨域的时候会用到corsConfig;总结:在Spring创建Bean的过程中,通过执行InitializingBean的扩展点方法完成RequestMapping和HandlerMethod的对应关系,在PropertiesSet中解析关系,并注册到mapper注册中心,完成准备工作!3、准备好后,如何执行?篇幅太长,想知道接下来会发生什么,敬请期待下一篇Spring源码文章列表细目:https://segmentfault.com/a/11...