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

相关推荐
Johny_Zhao3 小时前
Docker + CentOS 部署 Zookeeper 集群 + Kubernetes Operator 自动化运维方案
linux·网络安全·docker·信息安全·zookeeper·kubernetes·云计算·系统运维
幽络源小助理7 小时前
SpringBoot基于Mysql的商业辅助决策系统设计与实现
java·vue.js·spring boot·后端·mysql·spring
wfsm10 小时前
spring事件使用
java·后端·spring
Exclusive_Cat14 小时前
SpringMVC参数接收与数据返回详解
spring·mvc
ChinaRainbowSea15 小时前
补充:问题:CORS ,前后端访问跨域问题
java·spring boot·后端·spring
半新半旧15 小时前
Redis集群和 zookeeper 实现分布式锁的优势和劣势
redis·分布式·zookeeper
hqxstudying17 小时前
java依赖注入方法
java·spring·log4j·ioc·依赖
春生野草17 小时前
关于SpringMVC的整理
spring
Bug退退退12318 小时前
RabbitMQ 高级特性之重试机制
java·分布式·spring·rabbitmq
hello早上好19 小时前
CGLIB代理核心原理
java·spring