SpringMVC源码解析

springmvc处理请求的流程:

问题一、SpringMVC的八大组件何时初始化?

利用Spring事件监听机制。Tomcat启动,触发了DispatchServlet的初始化,初始化结束后,容器会发送事件,调用方法将从容器中获取的bean赋值到DispatchServlet的八大组件中。

问题二、SpringMVC的八大组件初始化的时候都能获取到值吗?

文件处理器不能,其他都能。所以要使用文件处理功能 需要引入额外的包。

java 复制代码
    //初始化八大组件
	protected void initStrategies(ApplicationContext context) {
		initMultipartResolver(context);  //容器中有就赋值  没有就没有
		initLocaleResolver(context);   //容器中有就赋值,容器中没有就创建默认的
		initThemeResolver(context);
		initHandlerMappings(context);
		initHandlerAdapters(context);
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);  //这个没啥用
		initViewResolvers(context);
		initFlashMapManager(context);
	}
java 复制代码
//文件处理器 : 如果容器里有就用默认的。 没有就没有
	private void initMultipartResolver(ApplicationContext context) {
		try {
			this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
			if (logger.isTraceEnabled()) {
				logger.trace("Detected " + this.multipartResolver);
			}
			else if (logger.isDebugEnabled()) {
				logger.debug("Detected " + this.multipartResolver.getClass().getSimpleName());
			}
		}
		catch (NoSuchBeanDefinitionException ex) {
			// Default is no multipart resolver.
			this.multipartResolver = null;
			if (logger.isTraceEnabled()) {
				logger.trace("No MultipartResolver '" + MULTIPART_RESOLVER_BEAN_NAME + "' declared");
			}
		}
	}
java 复制代码
//举例一个主题解析器。从容器中获取。有就赋值。没有就获取默认的
	private void initThemeResolver(ApplicationContext context) {
		try {
			this.themeResolver = context.getBean(THEME_RESOLVER_BEAN_NAME, ThemeResolver.class);
			if (logger.isTraceEnabled()) {
				logger.trace("Detected " + this.themeResolver);
			}
			else if (logger.isDebugEnabled()) {
				logger.debug("Detected " + this.themeResolver.getClass().getSimpleName());
			}
		}
		catch (NoSuchBeanDefinitionException ex) {
			// We need to use the default.
			this.themeResolver = getDefaultStrategy(context, ThemeResolver.class);
			if (logger.isTraceEnabled()) {
				logger.trace("No ThemeResolver '" + THEME_RESOLVER_BEAN_NAME +
						"': using default [" + this.themeResolver.getClass().getSimpleName() + "]");
			}
		}
	}

问题三、前端发送一个请求,怎么找到对应的处理类的?

1.容器启动完成,发送事件,调用DispatcherServlet的初始化八大组件方法。

2.初始化HandlerMappings的时候,在创建完对应的Bean后,会执行InitialzingBean的afterPropertiesSet方法:会将请求的路径以及对应的处理方法存储在一个map里面 。

3.前端发送请求后,会从这个map里找对应的目标方法,并且找到对应的拦截器链。

java 复制代码
	public final HandlerExecutionChain  getHandler(HttpServletRequest request) throws Exception {
		//找到请求对应的目标方法
		Object handler = getHandlerInternal(request);
		if (handler == null) {
			handler = getDefaultHandler();
		}
		if (handler == null) {
			return null;
		}
		// Bean name or resolved handler?
		if (handler instanceof String handlerName) {
			handler = obtainApplicationContext().getBean(handlerName);
		}

		// Ensure presence of cached lookupPath for interceptors and others
		if (!ServletRequestPathUtils.hasCachedPath(request)) {
			initLookupPath(request);
		}
        //找到请求的目标方法后,还要构造一个处理器链(把拦截器加进去)
		HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

		if (logger.isTraceEnabled()) {
			logger.trace("Mapped to " + handler);
		}
		else if (logger.isDebugEnabled() && !DispatcherType.ASYNC.equals(request.getDispatcherType())) {
			logger.debug("Mapped to " + executionChain.getHandler());
		}

		if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
			CorsConfiguration config = getCorsConfiguration(handler, request);
			if (getCorsConfigurationSource() != null) {
				CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
				config = (globalConfig != null ? globalConfig.combine(config) : config);
			}
			if (config != null) {
				config.validateAllowCredentials();
			}
			executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
		}

		return executionChain;
	}
java 复制代码
//寻找目标方法
	protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
		List<Match> matches = new ArrayList<>();
		List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
		if (directPathMatches != null) {
			addMatchingMappings(directPathMatches, matches, request);
		}
		if (matches.isEmpty()) {
			addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
		}
		if (!matches.isEmpty()) {
			Match bestMatch = matches.get(0);
			if (matches.size() > 1) {
				Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
				matches.sort(comparator);
				bestMatch = matches.get(0);
				if (logger.isTraceEnabled()) {
					logger.trace(matches.size() + " matching mappings: " + matches);
				}
				if (CorsUtils.isPreFlightRequest(request)) {
					for (Match match : matches) {
						if (match.hasCorsConfig()) {
							return PREFLIGHT_AMBIGUOUS_MATCH;
						}
					}
				}
				else {
					Match secondBestMatch = matches.get(1);
					if (comparator.compare(bestMatch, secondBestMatch) == 0) {
						Method m1 = bestMatch.getHandlerMethod().getMethod();
						Method m2 = secondBestMatch.getHandlerMethod().getMethod();
						String uri = request.getRequestURI();
						throw new IllegalStateException(
								"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
					}
				}
			}
			request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
			handleMatch(bestMatch.mapping, lookupPath, request);
			return bestMatch.getHandlerMethod();
		}
		else {
			return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
		}
	}
java 复制代码
//构造拦截器链
	protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
		HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain handlerExecutionChain ?
				handlerExecutionChain : new HandlerExecutionChain(handler));

		for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
			if (interceptor instanceof MappedInterceptor mappedInterceptor) {
				if (mappedInterceptor.matches(request)) {
					chain.addInterceptor(mappedInterceptor.getInterceptor());
				}
			}
			else {
				chain.addInterceptor(interceptor);
			}
		}
		return chain;
	}

问题四、HandlerAdapters是干啥的?

1.根据参数类型,选择合适的参数处理器,处理参数。

2.使用method.invoke()反射执行目标方法

3.根据返回值类型,选择合适的返回值处理器处理返回值。

问题五、SpringMVC视图解析器的原理?

SpringMVC用视图解析器将我们返回的字符串解析成真正的view对象进行渲染。

问题六、SpringMVC的统一异常一般是怎么处理的?

一般使用@ControllerAdvice + @ExceptionHandle进行处理的。

相关推荐
Flynn Liang1 小时前
Spring AI 多模态实战(下):文生图 + TTS 语音合成(含 2.0 流式音频分片)
人工智能·spring·文生图·音视频·语音合成·tts·springai
步行cgn2 小时前
Spring MVC 执行流程详解:一次请求的完整旅程
java·spring·mvc
步行cgn2 小时前
Spring MVC 九大核心角色详解:请求处理链路中的每一个关键组件
java·spring·mvc
Félix2513 小时前
Nacos学习笔记(SpringBoot/SpringCloud)
java·spring boot·spring
七夜zippoe3 小时前
LangChain4j vs Spring AI:Java AI 框架怎么选(对比表)
java·人工智能·spring·ai·langchain4j
Flynn Liang4 小时前
让 AI 查订单、查库存、发消息:Spring AI 自定义工具(Tool)实战与工具描述工程
java·人工智能·spring·函数调用·springai·tool·自定义工具
萧瑟余晖4 小时前
Spring Integration企业集成概览详解
java·后端·spring
七夜zippoe5 小时前
Spring AI 函数调用:让 Java 方法变成 Agent 的工具(实战全流程)
java·人工智能·spring·ai·agent·函数调用
萧瑟余晖5 小时前
Spring Batch 高级特性与实践详解
java·spring·batch
她说..5 小时前
常见设计模式-观察者模式
java·spring boot·spring·观察者模式·设计模式