Анализ исходного кода SpringMVC

Spring MVC

Анализ исходного кода SpringMVC

Spring Web MVC — это исходная веб-инфраструктура, построенная на API сервлетов и с самого начала включенная в структуру Spring. Его официальное название «Spring Web MVC» происходит от имени его исходного модуля (Spring-webmvc), но его более распространенное название — «Spring MVC».В этом разделе представлен Spring Web MVC.

(1) Новые функции сервлета 3.0
  • 在这里插入图片描述

Как видно из рисунка выше, сервлет 3.0 предоставляет нам очень мощную спецификацию.Если мы будем следовать этой спецификации, мы можем удалить web.xml при запуске tomcat, а также можем инициализировать среду Spring.

  1. Определена новая спецификация, то есть в папке META-INF/services файла ресурсов находится файл с именем javax.servlet.ServletContainerInitializer, который определяет полное имя класса вашего собственного класса. В то же время этот класс реализует интерфейс javax.servlet.ServletContainerInitializer и переопределяет метод onStartup.
  2. В соответствии с приведенной выше спецификацией все серверы сервлетов, соответствующие этой спецификации, такие как tomcat, будут отражать и выполнять метод onStartup() этого класса при запуске службы.
  3. Благодаря этой новой спецификации нам не нужно следовать традиционному методу, нам нужно инициализировать spring и другие конфигурации и среды в файле web.xml, чтобы мы могли достичь нулевой конфигурации, и springboot реализует нулевую конфигурацию в соответствии с этой идеей. .
(2) Имитация нулевой конфигурации SpringBoot, встроенный tomcat
  • 在这里插入图片描述

На приведенном выше рисунке вы можете увидеть симуляцию нулевой конфигурации springboot и встроенного tomcat, основные моменты, на которые следует обратить внимание:

  1. Разница между tomcat.addContext и tomcat.addWebapp:

(1) addWebapp указывает, что проект является веб-проектом.Когда tomcat запускается, он по умолчанию загружает парсер представления jsp, а затем сообщает об ошибке, если зависимость парсера представления jsp не добавлена. (2) addContext означает только добавление контекста в каталог webapps tomcat, tomcat не будет загружать парсер представления jsp и не будет сообщать об ошибке, что парсер представления jsp не может быть найден. (3) Springboot в основном больше не использует технологию jsp по умолчанию, такую ​​как: thymeleaf, freemarker... и т. д., поэтому нижний уровень springboot определенно не будет использовать метод addWebapp.

  1. Спецификации новых возможностей сервлета 3.0, упомянутые выше, здесь не используются, а в основном реализован интерфейс WebApplicationInitializer, код этого класса должен выполняться при старте tomcat, а также должен выполняться в случае веб-проекта. То есть в случае использования addContext наш класс выполняться не будет. Таким образом, единственный способ здесь - одновременно инициализировать среды spring ioc, spring mvc и tomcat с помощью мягкого метода.

Прежде чем приступить к анализу исходного кода spring mvc, мы должны иметь такое понятие:

  1. Запрос, инициированный браузером, может запрашивать только метод сервлета, он не может напрямую запрашивать метод класса java, то есть метод класса контроллера, который мы часто используем.
  2. Затем инфраструктура Spring MVC может выполнить запрос к методу соответствующего класса контроллера.Он должен сначала пропустить запрос к сервлету, а затем сервлет вызывает метод нашего класса контроллера.

запрос ----> !servlet.class (это невозможно) запрос ----> сервлет ----> контроллер (это может быть только вызов метода, иначе он не может быть реализован) (вызов метода, нижний слой должен быть технологией отражения: indexController:index())

  1. Сервлет DispatcherServlet является основным классом Spring MVC.
(3) Анализ исходного кода Spring MVC
  • Первое изображение: основная блок-схема SpringMVC
  • 在这里插入图片描述

Суммировать: (1) Первый запрос на вход в DispatcherServlet, и DispatcherServlet извлекает соответствующий обработчик из HandlerMappings. (2) В это время получается только соответствующий Handle, а затем необходимо найти соответствующий адаптер, а именно: HandlerAdapter. (3) Когда соответствующий HandlerAdapter получен, пришло время вызвать соответствующий обработчик для обработки бизнес-логики. (В это время наш контроллер фактически был выполнен) После завершения выполнения возвращается ModeAndView (4) В это время переданный нам ViewResolver находит соответствующее представление по имени представления и возвращает его. (5) Наконец визуализируйте представление и верните визуализированное представление --> ответьте на запрос.

3.1 Фаза инициализации Spring MVC

С чего начать? Благодаря приведенному выше анализу мы знаем, что основным классом Spring MVC является DispatcherServlet, который является классом сервлета, а затем посмотрите на этот класс, начиная с метода инициализации этого класса.

  • Структурная схема класса DispatcherServlet
  • 在这里插入图片描述
  • Глядя на класс DispatcherServlet, если нет метода инициализации, то вы можете найти только родительский класс этого класса.
# 1.执行父类HttpServletBean的init()方法
//tomcat启动,就会执行该方法,初始化DispatcherServlet
@Override
public final void init() throws ServletException {	
	// Set bean properties from init parameters.
	PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
	if (!pvs.isEmpty()) {
		try {
			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
			ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
			bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
			initBeanWrapper(bw);
			bw.setPropertyValues(pvs, true);
		}
		catch (BeansException ex) {
			throw ex;
		}
	}
	//初始化web环境(重要)
	initServletBean();
}

# 2.执行子类FrameworkServlet类的initServletBean()方法
@Override
protected final void initServletBean() throws ServletException {
	getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");	
	long startTime = System.currentTimeMillis();
	try {
		//初始化web环境
		this.webApplicationContext = initWebApplicationContext();
		initFrameworkServlet();
	}
	catch (ServletException | RuntimeException ex) {
		throw ex;
	}
}

# 3.执行FrameworkServlet类的initWebApplicationContext()方法
protected WebApplicationContext initWebApplicationContext() {
	WebApplicationContext rootContext =
			WebApplicationContextUtils.getWebApplicationContext(getServletContext());
	WebApplicationContext wac = null;

	if (this.webApplicationContext != null) {
		// A context instance was injected at construction time -> use it
		wac = this.webApplicationContext;
		if (wac instanceof ConfigurableWebApplicationContext) {
			ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
			if (!cwac.isActive()) {		
				if (cwac.getParent() == null) {
					cwac.setParent(rootContext);
				}
				//配置和刷新spring容器(重要)
				//这个无非就是初始化spring ioc的环境,创建bean和实例化bean等操作
				//这个方法最终也是调用refresh()方法,已在spring源码解析中解析过了
				configureAndRefreshWebApplicationContext(cwac);
			}
		}
	}
	if (wac == null) {		
		wac = findWebApplicationContext();
	}
	if (wac == null) {
		wac = createWebApplicationContext(rootContext);
	}
	if (!this.refreshEventReceived) {
		//初始化DispatcherServlet的配置initStrategies() (重点)
		onRefresh(wac);
	}
	if (this.publishContext) {
		// Publish the context as a servlet context attribute.
		String attrName = getServletContextAttributeName();
		getServletContext().setAttribute(attrName, wac);
	}
	return wac;
}

# 4.执行DispatcherServlet类的onRefresh()方法,初始化springmvc的配置
protected void onRefresh(ApplicationContext context) {
	//初始化springmvc的配置
	initStrategies(context);
}

(1) С помощью приведенного выше анализа кода можно получить, что выполняется метод инициализации DispatcherServlet, выполняется метод инициализации HttpServletBean родительского класса, а затем вызывается метод initServletBean() FrameworkServlet. HttpServletBean#init() ---> FrameworkServlet#initServletBean() (2) Выполнение метода initWebApplicationContext() является инициализацией среды Spring ioc. Итак, вот вопрос интервью: в чем разница между контейнером Spring и контейнером Spring MVC? Благодаря анализу исходного кода нижний слой Spring и Spring MVC вызывают один и тот же метод refresh(), поэтому нет никакой разницы между контейнером Spring и контейнером Spring MVC, оба ссылаются на один и тот же контейнер. (3) Когда метод onRefresh() выполняется, он начинает инициализировать DispatcherServlet, то есть начинает инициализировать spring mvc.

# 1.执行DispatcherServlet类的initStrategies()方法
protected void initStrategies(ApplicationContext context) {
	initMultipartResolver(context);//上传文件
	initLocaleResolver(context);//国际化
	initThemeResolver(context);//前段的主题样式
	initHandlerMappings(context);//初始化HandlerMappings(请求映射器)重点
	initHandlerAdapters(context);//初始化HandlerAdapters(处理适配器)
	initHandlerExceptionResolvers(context);
	initRequestToViewNameTranslator(context);
	initViewResolvers(context);//视图转换器
	initFlashMapManager(context);//重定向数据管理器
}

# 2.执行initHandlerMappings()方法
private void initHandlerMappings(ApplicationContext context) {
	this.handlerMappings = null;
	if (this.detectAllHandlerMappings) {
		// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
		Map<String, HandlerMapping> matchingBeans =
				BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
		if (!matchingBeans.isEmpty()) {
			this.handlerMappings = new ArrayList<>(matchingBeans.values());			
			AnnotationAwareOrderComparator.sort(this.handlerMappings);
		}
	}
	else {
		try {
			HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
			this.handlerMappings = Collections.singletonList(hm);
		}		
	}
	//通过配置文件中的配置信息,得到handlerMappings		
	if (this.handlerMappings == null) {
		//使用defaultStrategies获取数据
		this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);			
	}
}

# 3.执行getDefaultStrategies()方法
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
	String key = strategyInterface.getName();
	// defaultStrategies 是DispatcherServlet.properties 配置文件,在static静态代码块初始化
	String value = defaultStrategies.getProperty(key);
	if (value != null) {
		String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
		List<T> strategies = new ArrayList<>(classNames.length);
		for (String className : classNames) {
			try {
				// 获取class字节码文件
				Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
				// 底层是通过调用spring的getBean的方式创建该对象(可以进行bean的属性装配)
				// 请求映射就是在这个方法实现装配的
				Object strategy = createDefaultStrategy(context, clazz);
				strategies.add((T) strategy);
			}			
		}
		return strategies;
	}
	else {
		return new LinkedList<>();
	}
}

(1) Метод initHandlerMappings предназначен для инициализации нашего handlerMapping (сопоставителя запросов). (2) Основная функция handlerMapping — найти метод контроллера, соответствующий пути запроса.

Например: запрошенный путь "/index", а затем handlerMapping при инициализации сохранил сопоставление пути запроса всех контроллеров в коллекции карт, когда придет запрос, он будет использовать "/index" в качестве ключа, из Найдите метод index соответствующего контроллера в коллекции карт.

(3) Здесь инициализируются handlerMappings По умолчанию есть два handlerMappings, которые получаются непосредственно в конфигурационном файле defaultStrategies. (4) Когда инициализируется значение defaultStrategies?

Глядя на исходный код, значение defaultStrategies инициализируется блоком статического кода класса DispatcherServlet. Всему миру известно, что при инициализации класса будет выполняться статический блок статического кода класса.

# 1.DispatcherServlet类的static静态代码块
static {
	try {
		/**
		 * 从属性文件加载默认策略实现
		 * 说白了这里的意思就是从DEFAULT_STRATEGIES_PATH这个文件当中拿出所有的配置
		 * 可以去数一下一共有8个: DispatcherServlet.properties == DEFAULT_STRATEGIES_PATH
		 */
		ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
		defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
	}
}

# 2.DispatcherServlet.properties文件
//这里就贴出HandlerMapping和HandlerAdapter的类
org.springframework.web.servlet.HandlerMapping=
	org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

org.springframework.web.servlet.HandlerAdapter=
	org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
	org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

Из конфигурационного файла DispatcherServlet.properties видно, что по умолчанию есть два handlerMappings: 1.BeanNameUrlHandlerMapping (в основном обрабатывает объект) 2.RequestMappingHandlerMapping (в основном метод обработки)

3.2 Анализ фазы запроса Spring MVC

Запрос от пользователя будет получен сервлетом, а затем шаг за шагом будет вызван метод doService DispatcherServlet.

# 1.DispatcherServlet类的doService()方法
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {	
	try {
		//核心方法(重点)
		doDispatch(request, response);
	}
	finally {
		if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
			// Restore the original attribute snapshot, in case of an include.
			if (attributesSnapshot != null) {
				restoreAttributesAfterInclude(request, attributesSnapshot);
			}
		}
	}
}

# 2.调用DispatcherServlet类的doDispatch()方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
	HttpServletRequest processedRequest = request;
	HandlerExecutionChain mappedHandler = null;
	boolean multipartRequestParsed = false;
	//异步编程
	WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
	try {
		//定义变量
		ModelAndView mv = null;
		Exception dispatchException = null;
		try {
			//检查请求中是否有文件上传操作
			processedRequest = checkMultipart(request);
			multipartRequestParsed = (processedRequest != request);

			//确定当前请求的处理程序(重点),推断controller和handler的类型,
			mappedHandler = getHandler(processedRequest);
			if (mappedHandler == null) {
				noHandlerFound(processedRequest, response);
				return;
			}

			//推断适配器,不同的controller类型,交给不同的适配器去处理
			//如果是一个bean,mappedHandler.getHandler()返回的是一个对象
			//如果是一个method,mappedHandler.getHandler()返回的是一个方法		
			HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());			
			//到这里,spring才确定我要怎么反射调用
			
			//前置拦截器处理
			if (!mappedHandler.applyPreHandle(processedRequest, response)) {
				return;
			}
			//通过适配器,处理请求(可以理解为,反射调用方法)(重点)
			// Actually invoke the handler.
			mv = ha.handle(processedRequest, response, mappedHandler.getHandler());			

			applyDefaultViewName(processedRequest, mv);
			mappedHandler.applyPostHandle(processedRequest, response, mv);
		}		
		processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
	}	
}

При анализе DispatcherServlet основным методом обработки запроса является doDispatch(), который в основном делится на несколько этапов: (1) Проверить, есть ли в запросе операция загрузки файла (2) Определить обработчик для обработки текущего запроса (выделение) (3) Определите адаптеры, различные типы контроллеров и передайте их другим адаптерам для обработки. (4) Выполнить предварительный перехватчик для обработки перехватчика (5) Через найденный HandlerAdapter отражается метод выполнения соответствующего контроллера бизнес-кода. (6) Вернуть результат.

# 1.DispatcherServlet类的getHandler()方法
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	if (this.handlerMappings != null) {
		//循环所有的HandlerMappings
		//this.handlerMappings这个是什么时候初始化的?(重点)
		//在handlerMappings初始化的时候
		for (HandlerMapping hm : this.handlerMappings) {			
			//把请求传过去看能不能得到一个handler
			//注意:怎么得到handler和handlerMapping自己实现的逻辑有关系
			HandlerExecutionChain handler = hm.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
	}
	return null;
}

# 2.执行到AbstractHandlerMapping的getHandler()方法
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	//获取handler(重点)
	Object handler = getHandlerInternal(request);
	if (handler == null) {
		handler = getDefaultHandler();
	}
	if (handler == null) {
		return null;
	}
	// Bean name or resolved handler?
	if (handler instanceof String) {
		String handlerName = (String) handler;
		handler = obtainApplicationContext().getBean(handlerName);
	}
	HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
	if (CorsUtils.isCorsRequest(request)) {
		CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
		CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
		CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
		executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
	}
	return executionChain;
}

(1) Метод getHandler() в основном проходит через инициализированные handlerMappings в инициализации DispatcherServlet. (2) Основная идея этого метода заключается в сопоставлении соответствующего контроллера для обработки по пути запроса. (3) SpringMVC поставляется с 2 HandlerMappings, из которых мы можем выбирать, а почему их 2?

  • Мы используем 2 способа регистрации контроллера:
  • (1) В форме bean-компонента: реализовать интерфейс Controller, переписать метод handleRequest, а путь запроса — «/test».
@Component("/test")
public class TesrController implements org.springframework.web.servlet.mvc.Controller{
	@Override
	public ModelAndView handleRequest(HttpServletRequest request, 
	HttpServletResponse	response) throws Exception {
		System.out.println("1");
		return null;
	}
}
  • (2) В форме аннотации:
@Controller
public class AnnotationController {
	@RequestMapping("/test2")
	public Object test(){
		System.out.println("test");
		return null;
	}
}

(1) После тестирования можно получить контроллер в виде Bean, которому соответствует BeanNameUrlHandlerMapping (2) Контроллер с методом аннотации соответствует RequestMappingHandlerMapping

  • Анализ исходного кода того, как BeanNameUrlHandlerMapping обрабатывает bean-компоненты:
# 1.执行到AbstractUrlHandlerMapping的getHandlerInternal()方法
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
	//获取请求的路径
	String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
	//找到对应的handler(重点)
	Object handler = lookupHandler(lookupPath, request);
	if (handler == null) {			
		Object rawHandler = null;
		if ("/".equals(lookupPath)) {
			rawHandler = getRootHandler();
		}
		if (rawHandler == null) {
			rawHandler = getDefaultHandler();
		}
		if (rawHandler != null) {
			// Bean name or resolved handler?
			if (rawHandler instanceof String) {
				String handlerName = (String) rawHandler;
				rawHandler = obtainApplicationContext().getBean(handlerName);
			}
			validateHandler(rawHandler, request);
			handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
		}
	}
	return handler;
}

# 2.执行到AbstractUrlHandlerMapping的lookupHandler()方法
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
	//通过请求的路径,在handlerMap中去匹配。
	//handlerMap这个值,什么时候填充值?在init初始化的时候,就已经存放在这个handlerMap种
	Object handler = this.handlerMap.get(urlPath);
	if (handler != null) {
		// Bean name or resolved handler?
		if (handler instanceof String) {
			String handlerName = (String) handler;
			handler = obtainApplicationContext().getBean(handlerName);
		}
		validateHandler(handler, request);
		return buildPathExposingHandler(handler, urlPath, urlPath, null);
	}
	....忽略....
}

(1) Контроллер в режиме Bean сопоставляет запрошенный путь с помощью handlerMap, что относительно просто. (2) Вопрос в том, когда было введено значение этого handlerMap?

С помощью анализа исходного кода BeanNameUrlHandlerMapping реализует интерфейс ApplicationContextAware. Если вы хорошо разбираетесь в исходном коде Spring, вы знаете, что при создании экземпляра bean-компонента Spring он вызывает метод setApplicationContext() этих классов.

# 1.执行父类的ApplicationObjectSupport的setApplicationContext()方法
public final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException {
	if (context == null && !isContextRequired()) {
		// Reset internal context state.
		this.applicationContext = null;
		this.messageSourceAccessor = null;
	}
	else if (this.applicationContext == null) {		
		this.applicationContext = context;
		this.messageSourceAccessor = new MessageSourceAccessor(context);
		//初始化ApplicationContext,就会执行到子类的方法(重点)
		initApplicationContext(context);
	}
}

# 2.执行到AbstractDetectingUrlHandlerMapping类的initApplicationContext()方法
@Override
public void initApplicationContext() throws ApplicationContextException {
	super.initApplicationContext();
	// 检测出handler
	detectHandlers();
}

# 3.执行到AbstractDetectingUrlHandlerMapping类的detectHandlers()方法
protected void detectHandlers() throws BeansException {
	ApplicationContext applicationContext = obtainApplicationContext();
	//获取spring ioc所有的beanName,然后判断beanName,那些是以 "/" 开头
	String[] beanNames = (this.detectHandlersInAncestorContexts ?
			BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
			applicationContext.getBeanNamesForType(Object.class));
	for (String beanName : beanNames) {
		//然后判断beanName,那些是以 "/" 开头
		String[] urls = determineUrlsForHandler(beanName);
		if (!ObjectUtils.isEmpty(urls)) {
			//注册handler(重点)
			registerHandler(urls, beanName);
		}
	}
}

# 4.执行到AbstractUrlHandlerMapping的registerHandler()方法
protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {
	Assert.notNull(urlPaths, "URL path array must not be null");
	for (String urlPath : urlPaths) {
		registerHandler(urlPath, beanName);
	}
}

# 5.AbstractUrlHandlerMapping的registerHandler()方法
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
	Object resolvedHandler = handler;
	//最终put到map集合中(省略其他无关代码)
	this.handlerMap.put(urlPath, resolvedHandler);
}

Анализ исходного кода компонентов обработки BeanNameUrlHandlerMapping на самом деле очень прост: (1) Когда класс инициализируется, все классы контроллеров, реализующие интерфейс контроллера, получают свои @Componet('/test') (2) Затем используйте «/test» в качестве ключа и класс контроллера в качестве значения и поместите его в набор карт. (3) Когда приходит запрос, получите uri запроса, найдите его на карте и найдите, что он соответствует

  • Анализ исходного кода обработки аннотаций RequestMappingHandlerMapping:
# 1.AbstractHandlerMethodMapping#getHandlerInternal
// 对于RequestMappingHandlerMapping,indexController.index(),方法的请求路径映射
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
	//获取请求路径
	String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
	this.mappingRegistry.acquireReadLock();
	try {
		//通过请求路径,获取handler
		HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);	
		return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
	}
	finally {
		this.mappingRegistry.releaseReadLock();
	}
}

# 2.AbstractHandlerMethodMapping#lookupHandlerMethod
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
	List<Match> matches = new ArrayList<>();
	//从mappingRegistry的urlLookup,匹配请求路径
	List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
	if (directPathMatches != null) {
		addMatchingMappings(directPathMatches, matches, request);
	}
	if (matches.isEmpty()) {
		// No choice but to go through all mappings...
		addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
	}

	if (!matches.isEmpty()) {
		Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
		matches.sort(comparator);
		if (logger.isTraceEnabled()) {
			logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches);
		}
		Match bestMatch = matches.get(0);
		if (matches.size() > 1) {
			if (CorsUtils.isPreFlightRequest(request)) {
				return PREFLIGHT_AMBIGUOUS_MATCH;
			}
			Match secondBestMatch = matches.get(1);
			if (comparator.compare(bestMatch, secondBestMatch) == 0) {
				Method m1 = bestMatch.handlerMethod.getMethod();
				Method m2 = secondBestMatch.handlerMethod.getMethod();
				throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
						request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
			}
		}
		handleMatch(bestMatch.mapping, lookupPath, request);
		//返回handler
		return bestMatch.handlerMethod;
	}
	else {
		return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
	}
}

# 3.AbstractHandlerMethodMapping.MappingRegistry#getMappingsByUrl
public List<T> getMappingsByUrl(String urlPath) {
	return this.urlLookup.get(urlPath);
}

RequestMappingHandlerMapping обрабатывает анализ исходного кода методов аннотации, что является более сложным.MappingRegistry используется для поддержки всех сопоставлений пути запроса. Инициализация MappingRegistry также выполняется при создании экземпляра компонента. Принцип также аналогичен предыдущему, все сопоставляется с набором карт. Так что никакого разбора здесь не будет.

Резюме: получить обработчик()

  • Следующим шагом будет поиск адаптера Appapter.
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
	if (this.handlerAdapters != null) {
		for (HandlerAdapter ha : this.handlerAdapters) {
			if (logger.isTraceEnabled()) {
				logger.trace("Testing handler adapter [" + ha + "]");
			}
			if (ha.supports(handler)) {
				return ha;
			}
		}
	}
}

На самом деле видно, что он проходит через наши адаптеры из свойства handlerAdapters Откуда берется этот handlerAdapters? Как и наш HandlerMappings, он прописан в его конфигурационном файле, о чем мы только что сказали.

Что же касается адаптера, то поговорим о нем в связке с обработчиком, как мы говорили в первоначальном резюме, мы только что нашли обработчик и теперь его нужно выполнить, но есть проблема, обработчиков больше одного, и соответствующий метод выполнения, естественно, отличается.В это время выходит понятие адаптера: схема выполнения, соответствующая разным обработчикам. Когда подходящий адаптер найден, он в основном закончен, потому что после вынесения некоторых суждений (оценка типа запроса и т. д.) он начинает выполнять ваш обработчик, и код выше:

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

Этот mv и есть наш ModlAndView, по сути, после выполнения этой строчки логика нашего контроллера выполнена, а остальное — найти отрисовку представления.

Суммировать: Фактически, ключевыми понятиями нашего SpringMVC являются Handler (процессор) и Adapter (адаптер). Найдите подходящий обработчик для обработки вашего контроллера с помощью ключа HandlerMappings. Затем найти подходящий HandlerAdapter через HandlerAdapters для выполнения логики в Handler, то есть Controller. Наконец вернемся к ModlAndView...

В целом исходный код springmvc все еще очень сложен, в этом блоге лишь приблизительно описан основной процесс выполнения. Адрес загрузки аннотаций к исходному коду:GitHub.com/Компьютер мистера Ли/Судный день…