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进行处理的。

相关推荐
砍材农夫1 小时前
物联网实战|Spring Boot MQTT平台 |emqx broker实战
spring boot·spring·spring cloud·mybatis
2401_894915534 小时前
GEO 定位优化源码搭建常见报错排查:数据库、伪静态、接口调试
java·数据库·网络协议·tcp/ip·spring·unity
wangjialelele6 小时前
Spring Cloud 全体系学习笔记(REST、Nacos、Feign、Gateway)
java·spring boot·笔记·学习·spring·spring cloud·gateway
玖石书6 小时前
ASP.NET Core迁移Spring对照系列:文件系统操作篇
spring·asp.net
xqqxqxxq6 小时前
吃透 Spring 高频注解(包含SpringMVC Spring Boot)
java·spring boot·spring
攻城有术18 小时前
专项攻克——spring、springMVC、springBoot、springCloud的启动流程
spring boot·spring·spring cloud
markinmarkin20 小时前
Spring 中有哪些方式可以把Bean 注入到IOC 容器?
java·spring
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ1 天前
Spring‑AI Document 对象 JSON 字段详解
人工智能·spring·json
麻雀聊技术1 天前
一重启 AI 就失忆?用 Spring AI + PostgreSQL 3步搞定“长期记忆”持久化(附完整代码)
java·spring·openai
he___H1 天前
Spring-Configur注解
java·spring