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 小时前
Java深入解析篇十七之Spring Security
java·开发语言·spring
爱吃面的猫4 小时前
大数据Kafka3.x之——Kafka3.3.0安装与使用(详细)
大数据·分布式·zookeeper
Flittly7 小时前
【雕虫大技】Agent 动态 Skill 供应链安全加固(二):执行隔离沙箱实战
java·spring boot·spring
BUG指挥官1 天前
Sa-Token和Spring Security对比
java·后端·spring
geminigoth1 天前
Spring AI Alibaba 入门开发一(备份)
java·人工智能·spring
xbgRS1 天前
spring整合mybaits
java·spring·mybatis
ruleslol1 天前
静态依赖注入 VS 动态依赖查询
spring
墨雨晨曦881 天前
Spring AI总结
java·人工智能·spring
用户3126874877201 天前
Spring Boot 异常处理到底怎么玩的?从 DispatcherServlet 到全局兜底的全链路拆解
spring
不才不才不不才2 天前
Spring 源码系列(17): HandlerMapping 与 HandlerAdapter 两大体系
java·后端·spring