Проанализируйте принцип его работы из исходного кода SpringMvc.

Java

1. Использование MVC

Прежде чем изучать исходный код, давайте рассмотрим, как настроен springmvc ниже, что облегчит нам понимание исходного кода.

1.1 web.xml

<servlet>
	<servlet-name>mvc-dispatcher</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<!-- 配置springMVC需要加载的配置文件
		spring-dao.xml,spring-service.xml,spring-web.xml
		Mybatis - > spring -> springmvc
	 -->
	<init-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring/spring-*.xml</param-value>
	</init-param>
</servlet>
<servlet-mapping>
	<servlet-name>mvc-dispatcher</servlet-name>
	<!-- 默认匹配所有的请求 -->
	<url-pattern>/</url-pattern>
</servlet-mapping>

Стоит отметить, чтоcontextConfigLocationиDispatcherServletСсылка и конфигурация (используйте этот класс для перехвата запросов).

1.2 spring-web.xml

<!-- 配置SpringMVC -->
<!-- 1.开启SpringMVC注解模式 -->
<!-- 简化配置: 
	(1)自动注册DefaultAnootationHandlerMapping,AnotationMethodHandlerAdapter 
	(2)提供一些列:数据绑定,数字和日期的format @NumberFormat, @DateTimeFormat, xml,json默认读写支持 
-->
<mvc:annotation-driven />

<!-- 2.静态资源默认servlet配置
	(1)加入对静态资源的处理:js,gif,png
	(2)允许使用"/"做整体映射
 -->
 <mvc:default-servlet-handler/>
 
 <!-- 3.配置jsp 显示ViewResolver -->
 <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
 	<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
 	<property name="prefix" value="/WEB-INF/jsp/" />
 	<property name="suffix" value=".jsp" />
 </bean>
 
 <!-- 4.扫描web相关的bean -->
 <context:component-scan base-package="com.xxx.fantj.web" />

Стоит отметить, чтоInternalResourceViewResolver, это будет вModelAndViewИмя возвращаемой попытки имеет префиксprefixпрефикс, загружаемый послеsuffixУкажите суффикс.

Анализ исходного кода основной ветки SpringMvc

Обратитесь к диаграмме из «Spring в действии», чтобы лучше понять процесс выполнения:

В целом описанный выше процесс можно разделить на три блока:

  1. Map<url,Controller>построить (и поставитьWebApplicationContext)
  2. HttpRequestОбработка перехвата запроса URL в запросе (обработка DispatchServlet)
  3. вызов отраженияControllerСоответствующий метод обработки в , и вернуть представление

В этой статье основное внимание будет уделено этим трем частям анализа.

1. Создание карты

Когда контейнер инициализируется, соответствующие отношения между всеми URL-адресами и контроллерами будут установлены и сохранены в Map. Как это сохраняется?

Метод ApplicationObjectSupport #setApplicationContext
// 初始化ApplicationContext
@Override
public void initApplicationContext() throws ApplicationContextException {
	super.initApplicationContext();
	detectHandlers();
}
Метод AbstractDetectingUrlHandlerMapping #detectHandlers():
/**
 * 建立当前ApplicationContext 中的 所有Controller 和url 的对应关系
 * Register all handlers found in the current ApplicationContext.
 * <p>The actual URL determination for a handler is up to the concrete
 * {@link #determineUrlsForHandler(String)} implementation. A bean for
 * which no such URLs could be determined is simply not considered a handler.
 * @throws org.springframework.beans.BeansException if the handler couldn't be registered
 * @see #determineUrlsForHandler(String)
 */
protected void detectHandlers() throws BeansException {
	if (logger.isDebugEnabled()) {
		logger.debug("Looking for URL mappings in application context: " + getApplicationContext());
	}
	// 获取容器中的beanNames
	String[] beanNames = (this.detectHandlersInAncestorContexts ?
			BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
			getApplicationContext().getBeanNamesForType(Object.class));
	// 遍历 beanNames 并找到对应的 url
	// Take any bean name that we can determine URLs for.
	for (String beanName : beanNames) {
		// 获取bean上的url(class上的url + method 上的 url)
		String[] urls = determineUrlsForHandler(beanName);
		if (!ObjectUtils.isEmpty(urls)) {
			// URL paths found: Let's consider it a handler.
			// 保存url 和 beanName 的对应关系
			registerHandler(urls, beanName);
		}
		else {
			if (logger.isDebugEnabled()) {
				logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
			}
		}
	}
}
Метод определенияUrlsForHandler():

Этот метод имеет разные реализации в разных подклассах.То, что я анализирую здесь, этоDefaultAnnotationHandlerMappingРеализация класса, который в основном отвечает за обработку@RequestMappingДекларация в виде аннотации.

/**
 * 获取@RequestMaping注解中的url
 * Checks for presence of the {@link org.springframework.web.bind.annotation.RequestMapping}
 * annotation on the handler class and on any of its methods.
 */
@Override
protected String[] determineUrlsForHandler(String beanName) {
	ApplicationContext context = getApplicationContext();
	Class<?> handlerType = context.getType(beanName);
	// 获取beanName 上的requestMapping
	RequestMapping mapping = context.findAnnotationOnBean(beanName, RequestMapping.class);
	if (mapping != null) {
        // 类上面有@RequestMapping 注解
		this.cachedMappings.put(handlerType, mapping);
		Set<String> urls = new LinkedHashSet<String>();
		// mapping.value()就是获取@RequestMapping注解的value值
		String[] typeLevelPatterns = mapping.value();
		if (typeLevelPatterns.length > 0) {
            // 获取Controller 方法上的@RequestMapping
			String[] methodLevelPatterns = determineUrlsForHandlerMethods(handlerType);
			for (String typeLevelPattern : typeLevelPatterns) {
				if (!typeLevelPattern.startsWith("/")) {
					typeLevelPattern = "/" + typeLevelPattern;
				}
				for (String methodLevelPattern : methodLevelPatterns) {
					// controller的映射url+方法映射的url
					String combinedPattern = getPathMatcher().combine(typeLevelPattern, methodLevelPattern);
					// 保存到set集合中
					addUrlsForPath(urls, combinedPattern);
				}
				addUrlsForPath(urls, typeLevelPattern);
			}
			// 以数组形式返回controller上的所有url
			return StringUtils.toStringArray(urls);
		}
		else {
			// controller上的@RequestMapping映射url为空串,直接找方法的映射url
			return determineUrlsForHandlerMethods(handlerType);
		}
	}
	// controller上没@RequestMapping注解
	else if (AnnotationUtils.findAnnotation(handlerType, Controller.class) != null) {
		// 获取controller中方法上的映射url
		return determineUrlsForHandlerMethods(handlerType);
	}
	else {
		return null;
	}
}

Более подробный код относительно прост, и те, кто заинтересован, могут продолжать углубляться.

На этом этапе выполняется сопоставление контроллера и URL-адреса и анализируется процесс обработки запроса.

2. обработка запроса URL

Мы настроили в xmlDispatcherServletДля планировщика, так что давайте посмотрим на его код, вы можете Судя по названию, этоServlet, то его основной методdoService()

DispatcherServlet #doService():
/**
 * 将DispatcherServlet特定的请求属性和委托 公开给{@link #doDispatch}以进行实际调度。
 * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
 * for the actual dispatching.
 */
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
	if (logger.isDebugEnabled()) {
		String requestUri = new UrlPathHelper().getRequestUri(request);
		logger.debug("DispatcherServlet with name '" + getServletName() + "' processing " + request.getMethod() +
				" request for [" + requestUri + "]");
	}

    //在包含request的情况下保留请求属性的快照,
    //能够在include之后恢复原始属性。
	Map<String, Object> attributesSnapshot = null;
	if (WebUtils.isIncludeRequest(request)) {
		logger.debug("Taking snapshot of request attributes before include");
		attributesSnapshot = new HashMap<String, Object>();
		Enumeration attrNames = request.getAttributeNames();
		while (attrNames.hasMoreElements()) {
			String attrName = (String) attrNames.nextElement();
			if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
				attributesSnapshot.put(attrName, request.getAttribute(attrName));
			}
		}
	}

	// 使得request对象能供 handler处理和view处理 使用
	request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
	request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
	request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
	request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

	try {
		doDispatch(request, response);
	}
	finally {
		// 如果不为空,则还原原始属性快照。
		if (attributesSnapshot != null) {
			restoreAttributesAfterInclude(request, attributesSnapshot);
		}
	}
}

Видно, что после того, как он получает запрос, он в основном устанавливает некоторые объекты для запроса, чтобы облегчить обработку последующей работы (обработку обработчика и обработку представления). НапримерWebApplicationContext, который содержит то, что мы сделали на первом шагеcontrollerиurlкартографическая информация.

DispatchServlet # doDispatch()
/**
 * 控制请求转发
 * Process the actual dispatching to the handler.
 * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
 * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
 * to find the first that supports the handler class.
 * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
 * themselves to decide which methods are acceptable.
 * @param request current HTTP request
 * @param response current HTTP response
 * @throws Exception in case of any kind of processing failure
 */
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
	HttpServletRequest processedRequest = request;
	HandlerExecutionChain mappedHandler = null;
	int interceptorIndex = -1;

	try {

		ModelAndView mv;
		boolean errorView = false;

		try {
		    // 1. 检查是否是上传文件
			processedRequest = checkMultipart(request);

			// Determine handler for the current request.
            // 2. 获取handler处理器,返回的mappedHandler封装了handlers和interceptors
			mappedHandler = getHandler(processedRequest, false);
			if (mappedHandler == null || mappedHandler.getHandler() == null) {
			    // 返回404
				noHandlerFound(processedRequest, response);
				return;
			}

			// Apply preHandle methods of registered interceptors.
            // 获取HandlerInterceptor的预处理方法
			HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
			if (interceptors != null) {
				for (int i = 0; i < interceptors.length; i++) {
					HandlerInterceptor interceptor = interceptors[i];
					if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
						triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
						return;
					}
					interceptorIndex = i;
				}
			}

			// Actually invoke the handler.
            // 3. 获取handler适配器 Adapter
			HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
			// 4. 实际的处理器处理并返回 ModelAndView 对象
			mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

			// Do we need view name translation?
			if (mv != null && !mv.hasView()) {
				mv.setViewName(getDefaultViewName(request));
			}

			// HandlerInterceptor 后处理
			if (interceptors != null) {
				for (int i = interceptors.length - 1; i >= 0; i--) {
					HandlerInterceptor interceptor = interceptors[i];
					// 结束视图对象处理
					interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
				}
			}
		}
		catch (ModelAndViewDefiningException ex) {
			logger.debug("ModelAndViewDefiningException encountered", ex);
			mv = ex.getModelAndView();
		}
		catch (Exception ex) {
			Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
			mv = processHandlerException(processedRequest, response, handler, ex);
			errorView = (mv != null);
		}

		// Did the handler return a view to render?
		if (mv != null && !mv.wasCleared()) {
			render(mv, processedRequest, response);
			if (errorView) {
				WebUtils.clearErrorRequestAttributes(request);
			}
		}
		else {
			if (logger.isDebugEnabled()) {
				logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
						"': assuming HandlerAdapter completed request handling");
			}
		}

		// Trigger after-completion for successful outcome.
		// 请求成功响应之后的方法
		triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
	}

	catch (Exception ex) {
		// Trigger after-completion for thrown exception.
		triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
		throw ex;
	}
	catch (Error err) {
		ServletException ex = new NestedServletException("Handler processing failed", err);
		// Trigger after-completion for thrown exception.
		triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
		throw ex;
	}

	finally {
		// Clean up any resources used by a multipart request.
		if (processedRequest != request) {
			cleanupMultipart(processedRequest);
		}
	}
}

Метод в основном

  1. Получено из объекта запросаHandlerExecutionChain,HandlerExecutionChainОбъект содержит перехватчик-перехватчик и обработчик-обработчик. Если полученный объект пуст, отдайте егоnoHandlerFoundВернуться на страницу 404.
  2. Предварительная обработка перехватчика, если выполнение прошло успешно, перейти к 3
  3. Получить адаптер обработчика Адаптер
  4. Фактический обработчик обрабатывает и возвращает объект ModelAndView.

Вот некоторые основные детали метода:

DispatchServlet #doDispatch # noHandlerFoundОсновной исходный код:

response.sendError(HttpServletResponse.SC_NOT_FOUND);

DispatchServlet #doDispatch #getHandlerМетод действительно вызываетAbstractHandlerMapping #getHandlerметод, я разместил основной код:

// 拿到处理对象
Object handler = getHandlerInternal(request);
...
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
...
// 返回HandlerExecutionChain对象
return getHandlerExecutionChain(handler, request);

Как видите, сначала он получает объект обработчика из запроса, что доказывает, что предыдущийDispatchServlet #doServiceЗачемWebApplicationContextв объект запроса запроса.

в конце концов вернутьHandlerExecutionChainобъект.

3. Reflection вызывает метод обработки запроса и возвращает представление результата

В приведенном выше исходном коде фактический обработчик, который обрабатывает и возвращает объект ModelAndView, называетсяmv = ha.handle(processedRequest, response, mappedHandler.getHandler());Сюда. Метод состоит изAnnotationMethodHandlerAdapter #handle() #invokeHandlerMethod()реализация метода.

AnnotationMethodHandlerAdapter #handle() #invokeHandlerMethod()
/**
 * 获取处理请求的方法,执行并返回结果视图
 */
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
		throws Exception {

    // 1.获取方法解析器
	ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
	// 2.解析request中的url,获取处理request的方法
	Method handlerMethod = methodResolver.resolveHandlerMethod(request);
	// 3. 方法调用器
	ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
	ServletWebRequest webRequest = new ServletWebRequest(request, response);
	ExtendedModelMap implicitModel = new BindingAwareModelMap();
	// 4.执行方法(获取方法的参数)
	Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
	// 5. 封装成mv视图
	ModelAndView mav =
			methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
	methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
	return mav;
}

Есть два важных аспекта этого метода, а именноresolveHandlerMethodиinvokeHandlerMethod.

метод разрешенияHandlerMethod

methodResolver.resolveHandlerMethod(request): Получить класс контроллера и метод на@requestMapping value, сопоставьте URL-адрес запроса и найдите метод в контроллере, который обрабатывает запрос. Конкретная реализация окончательного сращивания:org.springframework.util.AntPathMatcher#combineметод.

метод invokeHandlerMethod

Из названия вы можете сказать, что он основан на отражении, так что же он делает.

Разберите параметры метода и вызовите метод.

//上面全都是为解析方法上的参数做准备
...
// 解析该方法上的参数
Object[] args = resolveHandlerArguments(handlerMethodToInvoke, handler, webRequest, implicitModel);
// 真正执行解析调用的方法
return doInvokeMethod(handlerMethodToInvoke, handler, args);
Метод invokeHandlerMethod #resolveHandlerArguments метод

Код немного длинный, поэтому позвольте мне кратко описать, что он делает.

  • Если параметры этого метода используют аннотации, проанализируйте аннотации, чтобы получить имена параметров, а затем получите имена параметров в запросе и присвойте значения, если они согласуются (подробный код находится вHandlerMethodInvoker#resolveRequestParam), затем поместите инкапсулированный объект в массив args[] и выполните возврат.
  • Если параметры этого метода не являются аннотациями, вам нужна структура asm (нижний уровень предназначен для чтения байт-кода), чтобы помочь получить имя параметра, а затем получить имя параметра в запросе, если они совпадают, назначьте value, а затем инкапсулировать его, поместить объект в массив args[] и вернуть его.
Метод invokeHandlerMethod Метод #doInvokeMethod
private Object doInvokeMethod(Method method, Object target, Object[] args) throws Exception {
	// 将一个方法设置为可调用,主要针对private方法
	ReflectionUtils.makeAccessible(method);
	try {
		// 反射调用
		return method.invoke(target, args);
	}
	catch (InvocationTargetException ex) {
		ReflectionUtils.rethrowException(ex.getTargetException());
	}
	throw new IllegalStateException("Should never get here");
}

На этом этапе вы можете вызвать соответствующий метод контроллера, соответствующий URL-адресу в запросе запроса.

Суммировать:

После прочтения у меня в голове должно быть очень сумбурно.Если у вас есть время, вам все равно нужно отладить его самостоятельно. Эта статья просто обобщает общую идею, поэтому функциональный исходный код не анализируется полностью.

На самом деле, понимание их является наиболее важным.

  1. Пользователь отправляет запрос на фронт-контроллер DispatcherServlet
  2. DispatcherServlet получает запрос и вызывает обработчик отображения HandlerMapping.
  3. Преобразователь процессора находит конкретный процессор в соответствии с URL-адресом запроса, генерирует объект процессора и перехватчик процессора (если есть) и возвращает его DispatcherServlet.
  4. DispatcherServlet вызывает обработчик через адаптер обработчика HandlerAdapter
  5. HandlerAdapter выполняет обработчик (обработчик, также называемый внутренним контроллером).
  6. Выполнение контроллера завершается и возвращается в ModelAndView
  7. HandlerAdapter возвращает результат выполнения обработчика ModelAndView в DispatcherServlet.
  8. DispatcherServlet передает ModelAndView преобразователю представления ViewReslover.
  9. ViewReslover возвращает конкретный объект View после синтаксического анализа
  10. DispatcherServlet визуализирует представление (то есть заполняет представление данными модели).
  11. DispatcherServlet отвечает пользователю

использованная литература:

Наконец, поделитесь волной преимуществ

нажмите, чтобы войти:Мой процесс изучения Java и краткое изложение знаний на собеседовании