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

相关推荐
CodeStats9 小时前
【Spring事务】Spring事务注解 @Transactional 完整体系:从 MySQL 隔离级别到 MyBatis 原理详解
java·spring·mybatis·事务·transactional
憧憬成为java架构高手的小白16 小时前
黑马八股--spring框架学习
java·学习·spring
Nuanyt20 小时前
SSM 学习记录 第二部分 Spring整合Mybatis&Junit AOP核心概念 Spring事务管理 SpringMVC 请求与响应 Rest风格
java·spring·junit·mybatis·restful
米码收割机21 小时前
【SSM】Spring MVC_MyBatis SSM商城系统(源码+论文)【独一无二】
spring·mvc·mybatis
用户31268748772021 小时前
@Autowired 注入的是代理对象?Spring AOP 代理选择原理一次讲透
java·spring
小罗水1 天前
第3章 从单体项目到 Spring Cloud 微服务
spring·spring cloud·微服务
Bug收容所1 天前
12305项目学习day5
java·spring boot·redis·mysql·spring·rocketmq
lichuangcsdn1 天前
【Spring AI 学习(三)】实现简单的对话
java·人工智能·学习·spring·spring ai
长不胖的路人甲1 天前
Spring Bean 完整生命周期
java·spring·rpc
辰海Coding1 天前
MiniSpring框架学习笔记-拦截器 :如何在方法前后进行拦截?
java·笔记·学习·spring·minispring