• 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 {
        @Autowired
        IClassifyService classifyService;
    
    
    
    
        @RequestMapping("/getClassify")
        @ResponseBody
        Object 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,其中包括RequestMappingHandlerMapping
    			Map<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

    @Override
    	public 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;
    
    		@Nullable
    		private 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和interceptors
    				mappedHandler = getHandler(processedRequest);
    				if (mappedHandler == null) {
    					//走没有Handler的逻辑,404
    					noHandlerFound(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注解的方法,并返回ModelAndView
    				mv = 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 afterCompletion
    				if (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
    	@Nullable
    	public 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 forward
    			return;
    		}
    
    		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页面
  • 相关阅读:
    Verilog的时间格式系统任务----$printtimescale、$timeformat
    论文笔记:Large Language Models Are Zero-Shot Time Series Forecasters
    Jtti:windows虚拟机如何设定永久静态路由
    CentOS7 自带防火墙+Nginx封堵高频访问的恶意IP
    金融租赁业务
    日期时间格式字符串“yyyy-MM-dd HH:mm:ss“的意思
    Qt Design Studio
    java中级面试题有哪些?java中级面试题大汇总
    微擎模块 美容美发营销版,最新版v1.9.6 原版可用
    华为OD机试2024(JS,C++,JAVA,PYTHON)-字符串摘要
  • 原文地址:https://blog.csdn.net/ludkjava/article/details/141088264