欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > IT业 > Spring MVC源码详解

Spring MVC源码详解

2024/10/25 19:30:04 来源:https://blog.csdn.net/ludkjava/article/details/141088264  浏览:    关键词:Spring MVC源码详解

什么是Spring MVC ?

Spring MVC就是Spring+MVC。
Spring就不介绍了,什么是MVC?

  • M:模型,javabean
  • V:视图,如jsp
  • C:控制层,如Controller、Servlet

SpringMVC请求处理流程

在这里插入图片描述

  1. 用户发送请求至前端控制器DispatcherServlet;
  2. DispatcherServlet收到请求调用HandlerMapping处理器映射器;
  3. 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)HandlerExecutionChain一并返回给DispatcherServlet;
  4. DispatcherServlet通过HandlerAdapter处理器适配器调用处理器,执行处理器(Controller,也叫后端控制器);
  5. Controller执行完成返回ModelAndView,并返回给HandlerAdapter,HandlerAdapter将结果返回给DispatcherServlet;
  6. DispatcherServlet将ModelAndView传给ViewReslover视图解析器,ViewReslover解析后返回具体View给DispatcherServlet;
  7. DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)后返回给给客户;

源码分析

代码调试准备

新建项目

新建Springboot项目,依赖如下:

		<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>

打断点

断点打在下面的位置:
org.springframework.web.servlet.DispatcherServlet#initStrategies:初始化阶段
org.springframework.web.servlet.DispatcherServlet#doDispatch:运行调用阶段。

项目调试启动时未进入上面的两个方法,当第一次调用Controller的请求时,会执行初始化阶段代码,每次调用Controller都会调用运行调用阶段代码。

初始化阶段

initStrategies方法初始化

第一次调用Controller时,会调用org.springframework.web.servlet.DispatcherServlet#initStrategies方法初始化SpringMVC的九大组件

protected void initStrategies(ApplicationContext context) {//多文件上传组件initMultipartResolver(context);//初始化本地语言环境initLocaleResolver(context);//初始化模版处理器initThemeResolver(context);//初始化处理器映射器initHandlerMappings(context);//初始化处理器适配器initHandlerAdapters(context);//初始化异常拦截器initHandlerExceptionResolvers(context);//初始化视图预处理器initRequestToViewNameTranslator(context);//初始化视图解析器initViewResolvers(context);//初始化FlashMap管理器initFlashMapManager(context);}

org.springframework.web.servlet.DispatcherServlet#initStrategies方法的调用链路如下:
在这里插入图片描述

HandlerMapping初始化

HandlerMapping是处理器映射器,简单讲是Controller中的方法与请求地址的映射关系,通过它可以找到Controller中的方法来处理请求。

什么是Handler?

Handler是controller中带请求路径的方法,最常用的是Controller中@RequestMapping注解标注的方法。如下面getProduct方法是Handler,loginPage方法不是Handler。

@Controller
@Slf4j
public class ClassifyController {@AutowiredIClassifyService classifyService;@RequestMapping("/getClassify")@ResponseBodyObject getProduct(){log.error("你妹的");return classifyService.selectByTenant("0000000001");}
//    @GetMapping("/index")public String loginPage(Model model){model.addAttribute("name","登科");return "welcom";}}

Spring中共四种Handler

  • 加RequestMapping注解的方法(常用的)
  • 实现Controller接口的Bean对象(古老的方法)
  • 实现HttpRequestHandler接口的Bean对象(古老的方法)
  • HandlerFunction对象(新的)

什么是HandlerMapping?

HandlerMapping是处理器映射器,简单讲是Controller中的方法与请求地址的映射关系,通过它可以找到Controller中的方法来处理请求。

三种常用的HandlerMapping

  • BeanNameUrlHandlerMapping:处理实现Controller、HttpRequestHandler接口的Bean对象对应的Handler
  • RequestMappingHandlerMapping:处理加RequestMapping注解的方法对应的Handler,也是最常用的。
  • RouterFunctionMapping:处理HandlerFunction对象对应的Handler

HandlerMapping初始化源码分析

private void initHandlerMappings(ApplicationContext context) {this.handlerMappings = null;if (this.detectAllHandlerMappings) {// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.//获取所有的HandlerMappings,其中包括RequestMappingHandlerMappingMap<String, HandlerMapping> matchingBeans =BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);if (!matchingBeans.isEmpty()) {//将获取到HandlerMapping赋值给DispatcherServlet.handlerMappings this.handlerMappings = new ArrayList<>(matchingBeans.values());// We keep HandlerMappings in sorted order.AnnotationAwareOrderComparator.sort(this.handlerMappings);}}else {try {HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);this.handlerMappings = Collections.singletonList(hm);}catch (NoSuchBeanDefinitionException ex) {// Ignore, we'll add a default HandlerMapping later.}}// Ensure we have at least one HandlerMapping, by registering// a default HandlerMapping if no other mappings are found.if (this.handlerMappings == null) {this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);if (logger.isTraceEnabled()) {logger.trace("No HandlerMappings declared for servlet '" + getServletName() +"': using default strategies from DispatcherServlet.properties");}}}

下面的代码获取了所有的HandlerMapping,其中包括RequestMappingHandlerMapping

Map<String, HandlerMapping> matchingBeans =BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);

以RequestMappingHandlerMapping为例,HandlerMapping中是如何为何请求地址与Handler之间的映射呢?

上面的方法通过BeanFactory获取RequestMappingHandlerMapping对应的实例,因为RequestMappingHandlerMapping是单例的,容器启动的时候会创建单例对象。查看RequestMappingHandlerMapping的父类,实现了InitializingBean接口,因此创建实例的时候会执行初始化方法,而绑定请求地址与Handler的逻辑就在初始化方法中。

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#afterPropertiesSet

public void afterPropertiesSet() {this.config = new RequestMappingInfo.BuilderConfiguration();this.config.setUrlPathHelper(getUrlPathHelper());this.config.setPathMatcher(getPathMatcher());this.config.setSuffixPatternMatch(useSuffixPatternMatch());this.config.setTrailingSlashMatch(useTrailingSlashMatch());this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());this.config.setContentNegotiationManager(getContentNegotiationManager());//跳转到父类的初始化方法super.afterPropertiesSet();}

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#afterPropertiesSet

@Overridepublic void afterPropertiesSet() {initHandlerMethods();}

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#initHandlerMethods

protected void initHandlerMethods() {for (String beanName : getCandidateBeanNames()) {if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {//绑定url与Handler的逻辑processCandidateBean(beanName);}}handlerMethodsInitialized(getHandlerMethods());}

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#processCandidateBean

protected void processCandidateBean(String beanName) {Class<?> beanType = null;try {beanType = obtainApplicationContext().getType(beanName);}catch (Throwable ex) {// An unresolvable bean type, probably from a lazy bean - let's ignore it.if (logger.isTraceEnabled()) {logger.trace("Could not resolve type for bean '" + beanName + "'", ex);}}//isHandler方法就是判断这个bean是否是Controller(有@Controller或者@RequestMapping注解)if (beanType != null && isHandler(beanType)) {detectHandlerMethods(beanName);}}

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#detectHandlerMethods
根据给定的Controller,找出这个Controller中的所有Handler,也就是带@RequestMapping注解的方法

protected void detectHandlerMethods(Object handler) {Class<?> handlerType = (handler instanceof String ?obtainApplicationContext().getType((String) handler) : handler.getClass());if (handlerType != null) {Class<?> userType = ClassUtils.getUserClass(handlerType);Map<Method, T> methods = MethodIntrospector.selectMethods(userType,(MethodIntrospector.MetadataLookup<T>) method -> {try {return getMappingForMethod(method, userType);}catch (Throwable ex) {throw new IllegalStateException("Invalid mapping on handler class [" +userType.getName() + "]: " + method, ex);}});if (logger.isTraceEnabled()) {logger.trace(formatMappings(userType, methods));}methods.forEach((method, mapping) -> {Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);registerHandlerMethod(handler, invocableMethod, mapping);});}}

最后通过org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#registerHandlerMethod方法将映射关系维护到org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#registry对应的Map中。key是mapping对象,其实就是@RequestMapping注解中对应的信息,value是MappingRegistration对象。

		private final Map<T, MappingRegistration<T>> registry = new HashMap<>();

MappingRegistration中

  • mapping是@RequestMapping注解中对应的信息

  • handlerMethod是Handler,也就是@RequestMapping注解的方法

  • directUrl是请求地址,这里观察是/getClassify

  • mappingName是映射的名称,这里观察室CC#getProduct

private static class MappingRegistration<T> {private final T mapping;private final HandlerMethod handlerMethod;private final List<String> directUrls;@Nullableprivate final String mappingName;............
}

HandlerMapping初始化总结

  • org.springframework.web.servlet.DispatcherServlet#initStrategies:初始化SpringMVC的九大组件。第一次调用请求Controller时,会初始化DispatcherServlet,因此会调用该方法
  • org.springframework.web.servlet.DispatcherServlet#initHandlerMappings:初始化处理器映射器
  • 获取所有的处理器映射器实例,并放入到org.springframework.web.servlet.DispatcherServlet#handlerMappings中,其实是List。以RequestMappingHandlerMapping这个处理器映射器为例子,因为RequestMappingHandlerMapping是单例、非延时加载,因此启动项目时会实例化该对象,handlerMappings是直接从缓存中获取的对象。
  • 实例化RequestMappingHandlerMapping对象时会调用初始化方法org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#afterPropertiesSet
  • 初始化方法会调用父类初始化方法org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#afterPropertiesSet
  • 父类初始化方法中会调用org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#initHandlerMethods
  • initHandlerMethods方法中会调用org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#processCandidateBean
  • processCandidateBean方法中,会判断是否为Controller,如果是则调用org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#detectHandlerMethods,该方法会查找Controller中所有的Handler,也就是该Controller所有标注@RequestMapping注解的方法,并解析注册到org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#registry中 。
  • 最终请求相关的信息和Handler就会绑定在一起,存储在HandlerMapping中。

运行调用阶段

随便调用一个Controller中的请求,会访问到org.springframework.web.servlet.DispatcherServlet#doDispatch方法中,调用链路如下:
在这里插入图片描述

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {ModelAndView mv = null;Exception dispatchException = null;try {//判断是否为文件上传请求processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// Determine handler for the current request.//根据请求获取handler,获取到的是HandlerExecutionChain,HandlerExecutionChain中包括handler和interceptorsmappedHandler = getHandler(processedRequest);if (mappedHandler == null) {//走没有Handler的逻辑,404noHandlerFound(processedRequest, response);return;}// Determine handler adapter for the current request.		//获取处理器适配器HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// Process last-modified header, if supported by the handler.String method = request.getMethod();boolean isGet = "GET".equals(method);if (isGet || "HEAD".equals(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}}if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// Actually invoke the handler.//调用handler,也就是Controller中对应的@RequestMapping注解的方法,并返回ModelAndViewmv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}//结果视图对象处理applyDefaultViewName(processedRequest, mv);mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {dispatchException = ex;}catch (Throwable err) {// As of 4.3, we're processing Errors thrown from handler methods as well,// making them available for @ExceptionHandler methods and other scenarios.dispatchException = new NestedServletException("Handler dispatch failed", err);}processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Throwable err) {triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler processing failed", err));}finally {if (asyncManager.isConcurrentHandlingStarted()) {// Instead of postHandle and afterCompletionif (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {// Clean up any resources used by a multipart request.if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}}}

根据请求获取Handler

下面的代码是根据请求获取handler,获取到的是HandlerExecutionChain,HandlerExecutionChain中包括handler和interceptors。

mappedHandler = getHandler(processedRequest);

org.springframework.web.servlet.DispatcherServlet#getHandler方法,遍历所有的handlerMapping,其中包括RequestMappingHandlerMapping,根据Request通过HandlerMapping获取Handler。

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {if (this.handlerMappings != null) {for (HandlerMapping mapping : this.handlerMappings) {HandlerExecutionChain handler = mapping.getHandler(request);if (handler != null) {return handler;}}}return null;}

org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler方法,根据请求获取HandlerExecutionChain。HandlerExecutionChain中包含handler和interceptors。因为初始化阶段初始化HandlerMapping时,已经将请求信息与Handler绑定到一起,所以这里可以通过绑定信息获取,不继续往下跟了。

根据请求获取HandlerAdapter

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

之前初始化阶段也初始化了HandlerAdapter,因此有下面的几个:
在这里插入图片描述
org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter方法中,循环遍历HandlerAdapter,匹配到则返回,这里匹配到了RequestMappingHandlerAdapter。

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {if (this.handlerAdapters != null) {//初始化阶段获取的实例for (HandlerAdapter adapter : this.handlerAdapters) {//遍历所有的HandlerAdapter ,匹配到则返回if (adapter.supports(handler)) {return adapter;}}}throw new ServletException("No adapter for handler [" + handler +"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");}

调用通过HandlerAdapter调用handler

这里执行了Controller中@RequestMapping注解的方法,走到了请求真正想处理的逻辑,并返回ModelAndView

// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle方法:

@Override@Nullablepublic final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {return handleInternal(request, response, (HandlerMethod) handler);}

调用org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal方法

调用org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod

上面的方法中通过下面代码,最终通过反射调用方法。

invocableMethod.invokeAndHandle(webRequest, mavContainer);

视图解析器返回View,并渲染页面

org.springframework.web.servlet.DispatcherServlet#processDispatchResult

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,@Nullable Exception exception) throws Exception {boolean errorView = false;if (exception != null) {//如果有异常则渲染异常页面if (exception instanceof ModelAndViewDefiningException) {logger.debug("ModelAndViewDefiningException encountered", exception);mv = ((ModelAndViewDefiningException) exception).getModelAndView();}else {Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);mv = processHandlerException(request, response, handler, exception);errorView = (mv != null);}}// Did the handler return a view to render?if (mv != null && !mv.wasCleared()) {//获取视图并进行渲染render(mv, request, response);if (errorView) {WebUtils.clearErrorRequestAttributes(request);}}else {if (logger.isTraceEnabled()) {logger.trace("No view rendering, null ModelAndView returned.");}}if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {// Concurrent handling started during a forwardreturn;}if (mappedHandler != null) {// Exception (if any) is already handled..mappedHandler.triggerAfterCompletion(request, response, null);}}

获取视图并进行页面渲染

if (mv != null && !mv.wasCleared()) {render(mv, request, response);if (errorView) {WebUtils.clearErrorRequestAttributes(request);}}

org.springframework.web.servlet.DispatcherServlet#render
方法中

//根据视图解析器获取视图,这里的视图解析器是初始化的时候通过initViewResolvers方法获取的
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,Locale locale, HttpServletRequest request) throws Exception {if (this.viewResolvers != null) {for (ViewResolver viewResolver : this.viewResolvers) {View view = viewResolver.resolveViewName(viewName, locale);if (view != null) {return view;}}}return null;}

org.springframework.web.servlet.DispatcherServlet#render方法中的下面代码,对视图进行渲染。

view.render(mv.getModelInternal(), request, response);

SpringMVC运行调用阶段源码总结

  • org.springframework.web.servlet.DispatcherServlet#getHandler:根据请求通过HandlerMapping获取Handler
  • org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter:根据Handler获取HandlerAdapter
  • org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle:调用HandlerAdap的handle方法,执行Handler(Controller中的方法)中的逻辑,返回ModelAndView。
  • org.springframework.web.servlet.DispatcherServlet#processDispatchResult:处理视图相关逻辑
  • org.springframework.web.servlet.DispatcherServlet#resolveViewName:根据视图解析器获取视图View
  • 调用View的render方法渲染jsp页面

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com