这一次搞懂SpringMVC原理说明
前言
前面几篇文章,学习了SpringIOC、Bean实例化过程、AOP、事务的源码和设计思想,了解了Spring的整体运行流程,但如果是web开发,那么必不可少的还有SpringMVC,本篇主要分析在请求调用过程中SpringMVC的实现原理,通过本篇要搞懂它是怎么解决请求、参数、返回值映射等问题的。
正文
请求入口
我们都知道前端调用后端接口时,都会通过Servlet进行转发,而Servlet的声明周期包含下面四个阶段:
实例化(new)
初始化(init)
执行(service调用doGet/doPost)
销毁(destroy)
前两个阶段在Spring启动阶段就做好了(init根据配置可能是第一次请求时才会调用),销毁是服务关闭的时候进行,本文主要分析的就是请求执行阶段。我们知道SpringMVC的核心就是DispatcherServlet,该类是对Servlet的扩展,所以直接从该类的service方法开始,但在此类中没有service方法,那肯定是在其父类中,我们先来看看其继承体系:
逐个往上找,在FrameworkServlet方法中就有一个service方法:
protectedvoidservice(HttpServletRequestrequest,HttpServletResponseresponse)
throwsServletException,IOException{
HttpMethodhttpMethod=HttpMethod.resolve(request.getMethod());
if(httpMethod==HttpMethod.PATCH||httpMethod==null){
processRequest(request,response);
}
else{
super.service(request,response);
}
}
protectedvoidservice(HttpServletRequestreq,HttpServletResponseresp)
throwsServletException,IOException
{
Stringmethod=req.getMethod();
if(method.equals(METHOD_GET)){
longlastModified=getLastModified(req);
if(lastModified==-1){
doGet(req,resp);
}else{
longifModifiedSince=req.getDateHeader(HEADER_IFMODSINCE);
if(ifModifiedSince
但其主要还是调用父类HttpServlet中的方法,而该类又会根据不同的请求方式会调到子类中,最后的核心方法就是DispatcherServlet中的doDispatch方法:
protectedvoiddoDispatch(HttpServletRequestrequest,HttpServletResponseresponse)throwsException{
HttpServletRequestprocessedRequest=request;
HandlerExecutionChainmappedHandler=null;
booleanmultipartRequestParsed=false;
//异步管理
WebAsyncManagerasyncManager=WebAsyncUtils.getAsyncManager(request);
try{
ModelAndViewmv=null;
ExceptiondispatchException=null;
try{
//文件上传
processedRequest=checkMultipart(request);
multipartRequestParsed=(processedRequest!=request);
//这个方法很重要,重点看
//Determinehandlerforthecurrentrequest.
mappedHandler=getHandler(processedRequest);
if(mappedHandler==null){
noHandlerFound(processedRequest,response);
return;
}
//获取跟HandlerMethod匹配的HandlerAdapter对象
//Determinehandleradapterforthecurrentrequest.
HandlerAdapterha=getHandlerAdapter(mappedHandler.getHandler());
//Processlast-modifiedheader,ifsupportedbythehandler.
Stringmethod=request.getMethod();
booleanisGet="GET".equals(method);
if(isGet||"HEAD".equals(method)){
longlastModified=ha.getLastModified(request,mappedHandler.getHandler());
if(newServletWebRequest(request,response).checkNotModified(lastModified)&&isGet){
return;
}
}
//前置过滤器,如果为false则直接返回
if(!mappedHandler.applyPreHandle(processedRequest,response)){
return;
}
//调用到Controller具体方法,核心方法调用,重点看看
//Actuallyinvokethehandler.
mv=ha.handle(processedRequest,response,mappedHandler.getHandler());
if(asyncManager.isConcurrentHandlingStarted()){
return;
}
applyDefaultViewName(processedRequest,mv);
//中置过滤器
mappedHandler.applyPostHandle(processedRequest,response,mv);
}
catch(Exceptionex){
dispatchException=ex;
}
catch(Throwableerr){
//Asof4.3,we'reprocessingErrorsthrownfromhandlermethodsaswell,
//makingthemavailablefor@ExceptionHandlermethodsandotherscenarios.
dispatchException=newNestedServletException("Handlerdispatchfailed",err);
}
//视图渲染及后置过滤器执行
processDispatchResult(processedRequest,response,mappedHandler,mv,dispatchException);
}
catch(Exceptionex){
triggerAfterCompletion(processedRequest,response,mappedHandler,ex);
}
catch(Throwableerr){
triggerAfterCompletion(processedRequest,response,mappedHandler,
newNestedServletException("Handlerprocessingfailed",err));
}
finally{
if(asyncManager.isConcurrentHandlingStarted()){
//InsteadofpostHandleandafterCompletion
if(mappedHandler!=null){
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest,response);
}
}
else{
//Cleanupanyresourcesusedbyamultipartrequest.
if(multipartRequestParsed){
cleanupMultipart(processedRequest);
}
}
}
}
MVC的所有处理逻辑都在这个方法中,先总结一下这个方法的实现逻辑,首先根据请求的url拿到缓存中的HandlerMethod对象和执行链对象,HandlerMethod中封装了controller对象、方法对象和方法参数等信息,执行链则是包含了一个个HandlerInterceptor拦截器;然后再通过HandlerMethod拿到对应的HandlerAdapter,这个对象的作用就是去适配我们的controller;准备工作做完后,首先会执行前置过滤,如果被拦截则直接返回,否则就去调用controller中的方法执行我们的业务逻辑并返回一个ModelView对象;接着执行中置过滤器,以及处理全局异常捕获器捕获到异常;最后进行视图渲染返回并执行后置过滤器进行资源释放等工作。
以上就是MVC的整体执行流程,下面就逐个来分析,首先进入getHandler方法:
protectedHandlerExecutionChaingetHandler(HttpServletRequestrequest)throwsException{
//handlerMappering实例
if(this.handlerMappings!=null){
for(HandlerMappingmapping:this.handlerMappings){
//获取HandlerMethod和过滤器链的包装类
HandlerExecutionChainhandler=mapping.getHandler(request);
if(handler!=null){
returnhandler;
}
}
}
returnnull;
}
是委托给HandlerMapping对象的,这是一个接口,主要的实现类是RequestMappingHandlerMapping,同样先来看看其继承体系:
这个类是管理请求和处理类之间的映射关系的,你是否疑惑它是在哪里实例化的呢?下面先来看看MVC组件的初始化。
组件初始化
这里我以自动化配置的注解方式说明,Spring提供了一个@EnableWebMvc,通过前面的学习我们知道在这个注解中必定导入了一个配置类,点进去可以看到是DelegatingWebMvcConfiguration,这个类就是负责MVC的组件和扩展实现的初始化,其本身我们先不看,先看其父类WebMvcConfigurationSupport,这个类我们应该不陌生,要做一些自定义扩展时就需要继承该类(如拦截器Interceptor),同样作用的类还有WebMvcConfigurerAdapter,这个类是对前者相对安全的扩展,为什么是相对安全呢?因为继承前者会导致自动配置失效,而使用后者则不必担心此问题,只需要在类上加上@EnableWebMvc注解。
在WebMvcConfigurationSupport中我们可以看到很多@Bean标注的方法,也就是mvc组件的实例化,这里主要看看requestMappingHandlerMapping,其余的可自行阅读理解,也就是一些Bean的注册:
publicRequestMappingHandlerMappingrequestMappingHandlerMapping(){
RequestMappingHandlerMappingmapping=createRequestMappingHandlerMapping();
mapping.setOrder(0);
mapping.setInterceptors(getInterceptors());
mapping.setContentNegotiationManager(mvcContentNegotiationManager());
mapping.setCorsConfigurations(getCorsConfigurations());
......省略
returnmapping;
}
这里主要看getInterceptors方法如何获取拦截器的:
protectedfinalObject[]getInterceptors(){
if(this.interceptors==null){
InterceptorRegistryregistry=newInterceptorRegistry();
//钩子方法,需要自己定义
addInterceptors(registry);
registry.addInterceptor(newConversionServiceExposingInterceptor(mvcConversionService()));
registry.addInterceptor(newResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider()));
this.interceptors=registry.getInterceptors();
}
returnthis.interceptors.toArray();
}
第一次进来会调用addInterceptors添加拦截器,这是一个模板方法,在子类DelegatingWebMvcConfiguration中实现:
privatefinalWebMvcConfigurerCompositeconfigurers=newWebMvcConfigurerComposite();
protectedvoidaddInterceptors(InterceptorRegistryregistry){
this.configurers.addInterceptors(registry);
}
publicvoidaddInterceptors(InterceptorRegistryregistry){
for(WebMvcConfigurerdelegate:this.delegates){
delegate.addInterceptors(registry);
}
}
可以看到最终是调用WebMvcConfigurer的addInterceptors方法,也就是我们对WebMvcConfigurerAdapter的自定义扩展。看到这里我们应该明白了MVC的组件是如何添加到IOC容器中的,但是DispatcherServlet又是怎么获取到它们的呢?回到之前的代码中,在DispatcherServlet这个类中有一个onRefresh方法,这个方法又调用了initStrategies方法完成了MVC九大组件的注册:
protectedvoidonRefresh(ApplicationContextcontext){
initStrategies(context);
}
protectedvoidinitStrategies(ApplicationContextcontext){
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
privatevoidinitHandlerMappings(ApplicationContextcontext){
this.handlerMappings=null;
if(this.detectAllHandlerMappings){
//FindallHandlerMappingsintheApplicationContext,includingancestorcontexts.
MapmatchingBeans=
BeanFactoryUtils.beansOfTypeIncludingAncestors(context,HandlerMapping.class,true,false);
if(!matchingBeans.isEmpty()){
this.handlerMappings=newArrayList<>(matchingBeans.values());
//WekeepHandlerMappingsinsortedorder.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else{
try{
HandlerMappinghm=context.getBean(HANDLER_MAPPING_BEAN_NAME,HandlerMapping.class);
this.handlerMappings=Collections.singletonList(hm);
}
catch(NoSuchBeanDefinitionExceptionex){
//Ignore,we'lladdadefaultHandlerMappinglater.
}
}
if(this.handlerMappings==null){
this.handlerMappings=getDefaultStrategies(context,HandlerMapping.class);
}
}
以initHandlerMappings为例,其它组件实现逻辑基本一样。首先从IOC容器中拿到handlerMappings的所有实现类(WebMvcConfigurationSupport中注入的对象就在这里被获取到),若没有,则从DispatcherServlet.properties配置文件中(这个配置在spring-webmvc工程下org/springframework/web/servlet/DispatcherServlet.properties)获取默认的配置:
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
但是onRefresh又是在什么时候调用的呢?有两个地方,一个是Servlet初始化时会调用到initWebApplicationContext进行容器的初始化,这个方法中就会触发onRefresh;另外还有一个,在FrameworkServlet中有一个onApplicationEvent方法,而这个方法又会被内部类ContextRefreshListener调用,这个类实现了ApplicationListener接口,表示会接收容器刷新事件。
以上就就是MVCHandlerMapping组件的初始化逻辑,其它组件实现逻辑相同,下面不再分析。
调用Controller
回到getHandler方法,其调用的是AbstractHandlerMapping类的方法:
publicfinalHandlerExecutionChaingetHandler(HttpServletRequestrequest)throwsException{
//根据请求的uri拿到对应的HandlerMethod对象
Objecthandler=getHandlerInternal(request);
if(handler==null){
handler=getDefaultHandler();
}
if(handler==null){
returnnull;
}
//Beannameorresolvedhandler?
if(handlerinstanceofString){
StringhandlerName=(String)handler;
handler=obtainApplicationContext().getBean(handlerName);
}
//获取HandlerMethod和过滤器链的包装类
HandlerExecutionChainexecutionChain=getHandlerExecutionChain(handler,request);
if(logger.isTraceEnabled()){
logger.trace("Mappedto"+handler);
}
elseif(logger.isDebugEnabled()&&!request.getDispatcherType().equals(DispatcherType.ASYNC)){
logger.debug("Mappedto"+executionChain.getHandler());
}
//是否是跨域请求,就是查看request请求头中是否有Origin属性
if(CorsUtils.isCorsRequest(request)){
//自定义的钩子方法获取跨域配置
CorsConfigurationglobalConfig=this.corsConfigurationSource.getCorsConfiguration(request);
//注解获取跨域配置
CorsConfigurationhandlerConfig=getCorsConfiguration(handler,request);
CorsConfigurationconfig=(globalConfig!=null?globalConfig.combine(handlerConfig):handlerConfig);
//这里设置了跨域的过滤器CorsInterceptor
executionChain=getCorsHandlerExecutionChain(request,executionChain,config);
}
returnexecutionChain;
}
先看AbstractHandlerMethodMapping.getHandlerInternal:
protectedHandlerMethodgetHandlerInternal(HttpServletRequestrequest)throwsException{
//从request对象中获取uri,/common/query2
StringlookupPath=getUrlPathHelper().getLookupPathForRequest(request);
this.mappingRegistry.acquireReadLock();
try{
//根据uri从映射关系中找到对应的HandlerMethod对象
HandlerMethodhandlerMethod=lookupHandlerMethod(lookupPath,request);
//把Controller类实例化
return(handlerMethod!=null?handlerMethod.createWithResolvedBean():null);
}
finally{
this.mappingRegistry.releaseReadLock();
}
}
protectedHandlerMethodlookupHandlerMethod(StringlookupPath,HttpServletRequestrequest)throwsException{
Listmatches=newArrayList<>();
//根据url拿到对应的RequestMappingInfo
ListdirectPathMatches=this.mappingRegistry.getMappingsByUrl(lookupPath);
if(directPathMatches!=null){
addMatchingMappings(directPathMatches,matches,request);
}
if(matches.isEmpty()){
//Nochoicebuttogothroughallmappings...
addMatchingMappings(this.mappingRegistry.getMappings().keySet(),matches,request);
}
if(!matches.isEmpty()){
Comparatorcomparator=newMatchComparator(getMappingComparator(request));
matches.sort(comparator);
MatchbestMatch=matches.get(0);
if(matches.size()>1){
if(logger.isTraceEnabled()){
logger.trace(matches.size()+"matchingmappings:"+matches);
}
if(CorsUtils.isPreFlightRequest(request)){
returnPREFLIGHT_AMBIGUOUS_MATCH;
}
MatchsecondBestMatch=matches.get(1);
//如果两个RequestMappinginfo什么都相同,报错
if(comparator.compare(bestMatch,secondBestMatch)==0){
Methodm1=bestMatch.handlerMethod.getMethod();
Methodm2=secondBestMatch.handlerMethod.getMethod();
Stringuri=request.getRequestURI();
thrownewIllegalStateException(
"Ambiguoushandlermethodsmappedfor'"+uri+"':{"+m1+","+m2+"}");
}
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE,bestMatch.handlerMethod);
handleMatch(bestMatch.mapping,lookupPath,request);
returnbestMatch.handlerMethod;
}
else{
returnhandleNoMatch(this.mappingRegistry.getMappings().keySet(),lookupPath,request);
}
}
privatevoidaddMatchingMappings(Collectionmappings,Listmatches,HttpServletRequestrequest){
for(Tmapping:mappings){
//拿到匹配的RequestMappingInfo对象,有可能url相同,@RequestMapping的属性(请求方式、参数等)匹配不上
Tmatch=getMatchingMapping(mapping,request);
if(match!=null){
//RequestMappingInfo对象和HandlerMethod对象封装到Match对象中,其实就是注解属性和Method对象的映射
matches.add(newMatch(match,this.mappingRegistry.getMappings().get(mapping)));
}
}
}
这里逻辑很简单,就是通过请求url从urlLookup中拿到对应的RequestMappingInfo(每一个@RequestMapping对应一个RequestMappingInfo对象)对象,再根据RequestMappingInfo对象从mappingLookup拿到对应的HandlerMethod并返回。
但这里你可能会比较好奇urlLookup和mappingLookup从哪里来的,仔细观察你会发现当前这个类实现了一个接口InitializingBean,实现了这个接口的类会在该类的Bean实例化完成后调用afterPropertiesSet方法,上面的映射关系就是在这个方法中做的。实际上这个方法不止完成了上面两个映射关系,还有下面两个:
corsLookup:handlerMethod->corsConfig
registry:RequestMappingInfo->MappingRegistration(包含url、handlerMethod、RequestMappingInfo、name等信息)
这里就不展开分析了,奉上一张时序图,读者可根据下面的时序图自行分析:
拿到HandlerMethod对象后,又会通过getHandlerExecutionChain方法去获取到所有的HandlerInterceptor拦截器对象,并连同HandlerMethod对象一起封装为HandlerExecutionChain。之后是获取跨域配置,这里不详细分析。
拿到HandlerExecutionChain对象后返回到doDispatch方法,又调用了getHandlerAdapter
方法拿到HandlerAdapter:
protectedHandlerAdaptergetHandlerAdapter(Objecthandler)throwsServletException{
//根据handlerMethod对象,找到合适的HandlerAdapter对象,这里用到了策略模式
if(this.handlerAdapters!=null){
for(HandlerAdapteradapter:this.handlerAdapters){
if(adapter.supports(handler)){
returnadapter;
}
}
}
}
这里的handlerAdapters变量值从哪里来?相信不用我再分析,主要看这里的设计思想,典型的策略模式。
之后调用完前置过滤器后,才是真正调用我们controller方法的逻辑,通过HandlerAdapter.handle去调用,最终会调用到ServletInvocableHandlerMethod.invokeAndHandle:
publicvoidinvokeAndHandle(ServletWebRequestwebRequest,ModelAndViewContainermavContainer,
Object...providedArgs)throwsException{
//具体调用逻辑,重点看
ObjectreturnValue=invokeForRequest(webRequest,mavContainer,providedArgs);
setResponseStatus(webRequest);
if(returnValue==null){
if(isRequestNotModified(webRequest)||getResponseStatus()!=null||mavContainer.isRequestHandled()){
mavContainer.setRequestHandled(true);
return;
}
}
elseif(StringUtils.hasText(getResponseStatusReason())){
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers!=null,"Noreturnvaluehandlers");
try{
//返回值处理
this.returnValueHandlers.handleReturnValue(
returnValue,getReturnValueType(returnValue),mavContainer,webRequest);
}
catch(Exceptionex){
if(logger.isTraceEnabled()){
logger.trace(formatErrorForReturnValue(returnValue),ex);
}
throwex;
}
}
这个方法里面主要看invokeForRequest和handleReturnValue的调用,前者是完成参数绑定并调用controller,后者则是对返回值进行处理并封装到ModelAndViewContainer中。先来看invokeForRequest:
publicObjectinvokeForRequest(NativeWebRequestrequest,@NullableModelAndViewContainermavContainer,
Object...providedArgs)throwsException{
//获取参数数组
Object[]args=getMethodArgumentValues(request,mavContainer,providedArgs);
if(logger.isTraceEnabled()){
logger.trace("Arguments:"+Arrays.toString(args));
}
returndoInvoke(args);
}
doInvoke就是完成反射调用,主要还是看参数绑定的实现逻辑,在getMethodArgumentValues方法中:
protectedObject[]getMethodArgumentValues(NativeWebRequestrequest,@NullableModelAndViewContainermavContainer,
Object...providedArgs)throwsException{
if(ObjectUtils.isEmpty(getMethodParameters())){
returnEMPTY_ARGS;
}
//入参的包装类,里面包装了参数类型,参数名称,参数注解等等信息
MethodParameter[]parameters=getMethodParameters();
Object[]args=newObject[parameters.length];
for(inti=0;i
参数、返回值解析
因为参数类型非常多,同时还会伴随各种注解,如:@RequestBody、@RequestParam、@PathVariable等,所以参数解析的工作是非常繁杂的,同时还要考虑到扩展性,所以SpringMVC依然采用了策略模式来完成对各种参数类型的解析绑定,其顶层接口就是HandlerMethodArgumentResolver,而默认SpringMVC提供的解析方式就高达20多种:
上面是类图,读者可根据自己熟悉的参数类型找到对应的类进行分析,最核心的还是要掌握这里的设计思想。
接着方法调用完成后就是对返回值的处理,同样的,返回值类型也是非常多,也可以使用各种注解标注,所以也是使用策略模式实现,其顶层接口是HandlerMethodReturnValueHandler,实现类如下:
调用完成之后就是执行后续操作了:执行中置过滤器、处理全局异常、视图渲染以及执行后置过滤器,这些与主流程没有太大关系,本篇不展开分析了,最后是MVC的执行时序图:
总结
本篇是Spring核心原理系列的最后一篇,前前后后花了一个月时间,终于从宏观上大致上理解了Spring的实现原理和运行机制,明白了之前项目中一些坑是如何产生的,最主要的是学到设计模式的运用以及如何利用Spring的一些常用的扩展点进行自定义扩展。但对于Spring这个庞大的体系来说,还有很多是要去理解学习的,尤其是设计思想,只有长期琢磨才能深刻的理解掌握。在我之前的文章中包括本篇还有很多没分析到的细节,在后面我会不定期分享出来。希望能给大家一个参考,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。