SpringMVC的设计与实现

SpringMVC的设计与实现

​ Spring MVC是MVC模式的一种实现 , 在Spring MVC 的使用中 , 除了之前讲过的ContextLoaderListener , 还有一个比较重要的类DispatchServlet , 在web.xml中也对其进行了配置 。从名字可以看出来这是一个Servlet , 这个Servlet实现的是Sun的J2EE核心模式中的前端控制器模式(Front Controller) , 所有的请求都要经过它来处理 , 进行转发、匹配、数据处理后 , 并转由页面进行展现 , 因此可以把它当做Spring MVC视线中最为核心的部分。

​ DispatchServlet与ContextLoaderListener 有没有什么联系呢 ?

​ 两者都是Spring上下文体系中非常重要的部分 , 在完成ContextLoaderListener的初始化后 , Web容器开始初始化DispatchServlet 。DispatchServlet会建立自己的上下文来持有Spring MVC的Bean对象 , 它会将ServletContext中的根上下文作为它的双亲上下文 , 并把自己持有的上下文也进行初始化并保存到ServletContext中。

​ DispatchServlet是怎么对上下文进行初始化的呢?先看看他的类继承关系图

在这里插入图片描述

​ DispatchServlet通过继承FramewServlet和HttpServletBean而继承了HttpServlet , 通过使用Servlet API来对HTTP请求进行响应 , 成为Spring MVC的前端处理器 , 同时成为MVC模块与Web容器集成的处理前端。

在这里插入图片描述

​ 从上图中可以看到Dispatchservlet的工作可以分为两部分: 1.初始化部分 , 由initServletBean()启动 , 通过initWebApplicationContext()方法最终调用DispatchServlet的initStrategies()方法 , 这部分做的就是上下文的初始化部分。2.对HTTP请求进行响应 , doService()方法会调用doDispatch()方法 , 在这个方法中进行了转发的操作。

1.Dispatchservlet的启动和初始化

​ 作为Servlet , 初始化时init()方法会被调用 , init方法中调用了子类的initServletBean()方法

public final void init() throws ServletException {
		if (logger.isDebugEnabled()) {
			logger.debug("Initializing servlet '" + getServletName() + "'");
		}

		// 获取Servlet的初始化参数,对Bean属性进行配置
		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) {
				if (logger.isErrorEnabled()) {
					logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
				}
				throw ex;
			}
		}

// 调用子类的initServletBean进行具体的初始化
    initServletBean();
	if (logger.isDebugEnabled()) {
			logger.debug("Servlet '" + getServletName() + "' configured successfully");
		}
	}
protected final void initServletBean() throws ServletException {
		getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
		if (this.logger.isInfoEnabled()) {
			this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
		}
		long startTime = System.currentTimeMillis();

		try {
            //初始化上下文
			this.webApplicationContext = initWebApplicationContext();
			initFrameworkServlet();
		}
		catch (ServletException ex) {
			this.logger.error("Context initialization failed", ex);
			throw ex;
		}
		catch (RuntimeException ex) {
			this.logger.error("Context initialization failed", ex);
			throw ex;
		}

		if (this.logger.isInfoEnabled()) {
			long elapsedTime = System.currentTimeMillis() - startTime;
			this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
					elapsedTime + " ms");
		}
	}
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()) {
					// The context has not yet been refreshed -> provide services such as
					// setting the parent context, setting the application context id, etc
					if (cwac.getParent() == null) {
						// The context instance was injected without an explicit parent -> set
						// the root application context (if any; may be null) as the parent
						cwac.setParent(rootContext);
					}
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		if (wac == null) {
			// No context instance was injected at construction time -> see if one
			// has been registered in the servlet context. If one exists, it is assumed
			// that the parent context (if any) has already been set and that the
			// user has performed any initialization such as setting the context id
			wac = findWebApplicationContext();
		}
		if (wac == null) {
			// 创建上下文的过程和IoC很相似 , 没有指定就创建默认的上下文
			wac = createWebApplicationContext(rootContext);
		}

		if (!this.refreshEventReceived) {
			// Either the context is not a ConfigurableApplicationContext with refresh
			// support or the context injected at construction time had already been
			// refreshed -> trigger initial onRefresh manually here.
			onRefresh(wac);
		}

		if (this.publishContext) {
			// Publish the context as a servlet context attribute.
			String attrName = getServletContextAttributeName();
			getServletContext().setAttribute(attrName, wac);
			if (this.logger.isDebugEnabled()) {
				this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
						"' as ServletContext attribute with name [" + attrName + "]");
			}
		}

		return wac;
	}

​ DispatchServlet持有自己的一个Servlet名称命名的IoC容器 , 这个IoC容器是一个WebApplicationContext对象 , SpringMVC的具体实现和Spring应用的实现并没有什么太大差别 . 但是MVC在对容器初始化完成之后会调用initStrategies()方法,来实现转发功能的初始化 , 可以从名称看出来 ,这个方法中初始化了支持request映射的HandlerMapping、视图、国际化的localResolver等 .

protected void initStrategies(ApplicationContext context) {
		initMultipartResolver(context);
		initLocaleResolver(context);
		initThemeResolver(context);
		initHandlerMappings(context);
		initHandlerAdapters(context);
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		initViewResolvers(context);
		initFlashMapManager(context);
	}	

​ initHandlerMappings方法并不是干初始化Mapping的工作的 , 它只是初始化handlerMappings属性的值 , 决定后续的转发是由哪个子类执行的

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());
				// We keep HandlerMappings in sorted order.
				AnnotationAwareOrderComparator.sort(this.handlerMappings);
			}
		}
		else {
			try {
				HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
				this.handlerMappings = Collections.singletonList(hm);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Ignore, we'll add a default HandlerMapping later.
			}
		}

		// 默认加载的是DispatcherServlet.properties文件中的内容
		if (this.handlerMappings == null) {
			this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
			if (logger.isDebugEnabled()) {
				logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
			}
		}
	}

​ DispatcherServlet.properties

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

至于Controller是什么时候转换成Mapping的 , 可以研究一下AbstractHandlerMethodMapping类 , 从名字可以看出来该类是将method作为handler来使用的

// @since 3.1  Spring3.1之后才出现,这个时候注解驱动也出来了
// 实现了initializingBean接口,其实主要的注册操作则是通过afterPropertiesSet()接口方法来调用的
// 它是带有泛型T的。
// T:包含HandlerMethod与传入请求匹配所需条件的handlerMethod的映射~
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
	// SCOPED_TARGET的BeanName的前缀
	private static final String SCOPED_TARGET_NAME_PREFIX = "scopedTarget.";
	private static final HandlerMethod PREFLIGHT_AMBIGUOUS_MATCH = new HandlerMethod(new EmptyHandler(), ClassUtils.getMethod(EmptyHandler.class, "handle"));
	// 跨域相关
	private static final CorsConfiguration ALLOW_CORS_CONFIG = new CorsConfiguration();
	static {
		ALLOW_CORS_CONFIG.addAllowedOrigin("*");
		ALLOW_CORS_CONFIG.addAllowedMethod("*");
		ALLOW_CORS_CONFIG.addAllowedHeader("*");
		ALLOW_CORS_CONFIG.setAllowCredentials(true);
	}
	
	// 默认不会去祖先容器里面找Handlers
	private boolean detectHandlerMethodsInAncestorContexts = false;
	// @since 4.1提供的新接口
	// 为处HandlerMetho的映射分配名称的策略接口   只有一个方法getName()
	// 唯一实现为:RequestMappingInfoHandlerMethodMappingNamingStrategy
	// 策略为:@RequestMapping指定了name属性,那就以指定的为准  否则策略为:取出Controller所有的`大写字母` + # + method.getName()
	// 如:AppoloController#match方法  最终的name为:AC#match 
	// 当然这个你也可以自己实现这个接口,然后set进来即可(只是一般没啥必要这么去干~~)
	@Nullable
	private HandlerMethodMappingNamingStrategy<T> namingStrategy;
	// 内部类:负责注册~
	private final MappingRegistry mappingRegistry = new MappingRegistry();

	// 此处细节:使用的是读写锁  比如此处使用的是读锁   获得所有的注册进去的Handler的Map
	public Map<T, HandlerMethod> getHandlerMethods() {
		this.mappingRegistry.acquireReadLock();
		try {
			return Collections.unmodifiableMap(this.mappingRegistry.getMappings());
		} finally {
			this.mappingRegistry.releaseReadLock();
		}
	}
	// 此处是根据mappingName来获取一个Handler  此处需要注意哦~~~
	@Nullable
	public List<HandlerMethod> getHandlerMethodsForMappingName(String mappingName) {
		return this.mappingRegistry.getHandlerMethodsByMappingName(mappingName);
	}
	// 最终都是委托给mappingRegistry去做了注册的工作   此处日志级别为trace级别
	public void registerMapping(T mapping, Object handler, Method method) {
		if (logger.isTraceEnabled()) {
			logger.trace("Register \"" + mapping + "\" to " + method.toGenericString());
		}
		this.mappingRegistry.register(mapping, handler, method);
	}
	public void unregisterMapping(T mapping) {
		if (logger.isTraceEnabled()) {
			logger.trace("Unregister mapping \"" + mapping + "\"");
		}
		this.mappingRegistry.unregister(mapping);
	}

	// 这个很重要,是初始化HandlerMethods的入口~~~~~
	@Override
	public void afterPropertiesSet() {
		initHandlerMethods();
	}
	// 看initHandlerMethods(),观察是如何实现加载HandlerMethod
	protected void initHandlerMethods() {
		// getCandidateBeanNames:Object.class相当于拿到当前容器(一般都是当前容器) 内所有的Bean定义信息
		// 如果阁下容器隔离到到的话,这里一般只会拿到@Controller标注的web组件  以及其它相关web组件的  不会非常的多的~~~~
		for (String beanName : getCandidateBeanNames()) {
			// BeanName不是以这个打头得  这里才会process这个BeanName~~~~
			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
				// 会在每个Bean里面找处理方法,HandlerMethod,然后注册进去
				processCandidateBean(beanName);
			}
		}
		// 略:它就是输出一句日志:debug日志或者trace日志   `7 mappings in 'requestMappingHandlerMapping'`
		handlerMethodsInitialized(getHandlerMethods());
	}

	// 确定指定的候选bean的类型,如果标识为Handler类型,则调用DetectHandlerMethods
	// isHandler(beanType):判断这个type是否为Handler类型   它是个抽象方法,由子类去决定到底啥才叫Handler~~~~
	// `RequestMappingHandlerMapping`的判断依据为:该类上标注了@Controller注解或者@Controller注解  就算作是一个Handler
	// 所以此处:@Controller起到了一个特殊的作用,不能等价于@Component的哟~~~~
	protected void processCandidateBean(String beanName) {
		Class<?> beanType = null;
		try {
			beanType = obtainApplicationContext().getType(beanName);
		} catch (Throwable ex) {
			// 即使抛出异常  程序也不会终止~
		}
		if (beanType != null && isHandler(beanType)) {
			// 这个和我们上篇博文讲述的类似,都属于detect探测系列~~~~
			detectHandlerMethods(beanName);
		}
	}

	// 在指定的Handler的bean中查找处理程序方法Methods  找打就注册进去:mappingRegistry
	protected void detectHandlerMethods(Object handler) {
		Class<?> handlerType = (handler instanceof String ?
				obtainApplicationContext().getType((String) handler) : handler.getClass());

		if (handlerType != null) {
			Class<?> userType = ClassUtils.getUserClass(handlerType);
		
			// 又是非常熟悉的方法:MethodIntrospector.selectMethods
			// 它在我们招@EventListener、@Scheduled等注解方法时已经遇到过多次
			// 此处特别之处在于:getMappingForMethod属于一个抽象方法,由子类去决定它的寻找规则~~~~  什么才算作一个处理器方法
			Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
					(MethodIntrospector.MetadataLookup<T>) method -> {
						try {
							return getMappingForMethod(method, userType);
						} catch (Throwable ex) {
							throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, ex);
						}
					});
			
			// 把找到的Method  一个个遍历,注册进去~~~~
			methods.forEach((method, mapping) -> {
				// 找到这个可调用的方法(AopUtils.selectInvocableMethod)
				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
				registerHandlerMethod(handler, invocableMethod, mapping);
			});
		}
	}
}

大致步骤: 找到容器中的所有bean , 如果bean是handler的话就就对bean进行解析 (是否是handler是交给子类去实现的) , 如果是handler获取到该bean中的所有method , (以RequestMappingHandlerMapping为例 ),如果方法上面有requestMapping,根据注解的信息生成RequestMappingInfo , 并且将方法上的信息与类上面的信息结合 , 将方法与mapping信息的对应关系存到Map中 ,遍历Map为method找到对应的可调用方法invocableMethod , 然后将方法注册到HandlerMapping中 , 注册存储的地方是AbstractHandlerMethodMapping.MappingRegistry:内部类注册中心

class MappingRegistry {
		// mapping对应的其MappingRegistration对象~~~
		private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
		// 保存着mapping和HandlerMethod的对应关系~
		private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
		// 保存着URL与匹配条件(mapping)的对应关系  当然这里的URL是pattern式的,可以使用通配符
		// 这里的Map不是普通的Map,而是MultiValueMap,它是个多值Map。其实它的value是一个list类型的值
		// 至于为何是多值?有这么一种情况  URL都是/api/v1/hello  但是有的是get post delete等方法   所以有可能是会匹配到多个MappingInfo的
		private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
		// 这个Map是Spring MVC4.1新增的(毕竟这个策略接口HandlerMethodMappingNamingStrategy在Spring4.1后才有,这里的name是它生成出来的)
		// 保存着name和HandlerMethod的对应关系(一个name可以有多个HandlerMethod)
		private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
		
		// 这两个就不用解释了
		private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
		// 读写锁~~~ 读写分离  提高启动效率
		private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

		... // 提供一些查找方法,都不是线程安全的
		
		// 读锁提供给外部访问,写锁自己放在内部即可~~~
		public void acquireReadLock() {
			this.readWriteLock.readLock().lock();
		}
		public void releaseReadLock() {
			this.readWriteLock.readLock().unlock();
		}

		// 注册Mapping和handler 以及Method    此处上写锁保证线程安全~
		public void register(T mapping, Object handler, Method method) {
			this.readWriteLock.writeLock().lock();
			try {
				// 此处注意:都是new HandlerMethod()了一个新的出来~~~~
				HandlerMethod handlerMethod = createHandlerMethod(handler, method);
				// 同样的:一个URL Mapping只能对应一个Handler
				// 这里可能会出现常见的一个异常信息:Ambiguous mapping. Cannot map XXX 
				assertUniqueMethodMapping(handlerMethod, mapping);
		
				// 缓存Mapping和handlerMethod的关系  
				this.mappingLookup.put(mapping, handlerMethod);

				// 保存url和RequestMappingInfo(mapping)对应关系
				// 这里注意:多个url可能对应着同一个mappingInfo呢~  毕竟@RequestMapping的url是可以写多个的~~~~
				List<String> directUrls = getDirectUrls(mapping);
				for (String url : directUrls) {
					this.urlLookup.add(url, mapping);
				}

				// 保存name和handlerMethod的关系  同样也是一对多
				String name = null;
				if (getNamingStrategy() != null) {
					name = getNamingStrategy().getName(handlerMethod, mapping);
					addMappingName(name, handlerMethod);
				}

				CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
				if (corsConfig != null) {
					this.corsLookup.put(handlerMethod, corsConfig);
				}

				// 注册mapping和MappingRegistration的关系
				this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
			}
			// 释放锁
			finally {
				this.readWriteLock.writeLock().unlock();
			}
		}

		// 相当于进行一次逆向操作~
		public void unregister(T mapping) { ... }
		...
	}

参考:https://cloud.tencent.com/developer/article/1497621

注册完成之后DispatchServlet的启动过程中的重要部分就结束了 ,下面看一下调用过程

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);

				// Determine handler for the current request.
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (logger.isDebugEnabled()) {
						logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
					}
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// Actually invoke the handler.
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}

				applyDefaultViewName(processedRequest, mv);
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			catch (Throwable err) {
				// As of 4.3, we're processing Errors thrown from handler methods as well,
				// making them available for @ExceptionHandler methods and other scenarios.
				dispatchException = new NestedServletException("Handler dispatch failed", err);
			}
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Throwable err) {
			triggerAfterCompletion(processedRequest, response, mappedHandler,
					new NestedServletException("Handler processing failed", err));
		}
		finally {
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				if (mappedHandler != null) {
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				// Clean up any resources used by a multipart request.
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}

DispatchServlet也是一个servlet ,我们知道servlet的调用方法入口是doService()方法,在doService()方法中又调用了dodispatch()方法,在这个这个方法中又调用了getHandler()方法 , 根据请求的路径找到对应的handler , 这个方法是请求分发过程中比较重要的部分

DispatchServlet类:

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		if (this.handlerMappings != null) {
			for (HandlerMapping hm : this.handlerMappings) {
				if (logger.isTraceEnabled()) {
					logger.trace(
							"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
				}
				HandlerExecutionChain handler = hm.getHandler(request);
				if (handler != null) {
					return handler;
				}
			}
		}
		return null;
	}

AbstractHandlerMapping类:

获取到handle并且把拦截器封装到HandlerExecutionChain中 , 以便后面使用

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) {
			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;
	}
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
	...
	@Override
	protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
		// 要进行匹配的  请求的URI path
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		this.mappingRegistry.acquireReadLock();
		try {
			//委托给方法lookupHandlerMethod() 去找到一个HandlerMethod去最终处理~
			HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
		}
		finally {
			this.mappingRegistry.releaseReadLock();
		}
	}
	@Nullable
	protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
		// Match是一个private class,内部就两个属性:T mapping和HandlerMethod handlerMethod
		List<Match> matches = new ArrayList<>();
		
		// 根据lookupPath去注册中心里查找mappingInfos,因为一个具体的url可能匹配上多个MappingInfo的
		// 至于为何是多值?有这么一种情况  URL都是/api/v1/hello  但是有的是get post delete等方法  当然还有可能是headers/consumes等等不一样,都算多个的  所以有可能是会匹配到多个MappingInfo的
		// 所有这个里可以匹配出多个出来。比如/hello 匹配出GET、POST、PUT都成,所以size可以为3
		List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
		
		if (directPathMatches != null) {
			// 依赖于子类实现的抽象方法:getMatchingMapping()  看看到底匹不匹配,而不仅仅是URL匹配就行
			// 比如还有method、headers、consumes等等这些不同都代表着不同的MappingInfo的
			// 最终匹配上的,会new Match()放进matches里面去
			addMatchingMappings(directPathMatches, matches, request);
		}
	
		// 当还没有匹配上的时候,别无选择,只能浏览所有映射
		// 这里为何要浏览所有的mappings呢?而不是报错404呢?这里我有点迷糊,愿有知道的指明这个设计意图~~~
		if (matches.isEmpty()) {
			// No choice but to go through all mappings...
			addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
		}

		// 单反只要找到了一个匹配的  就进来这里了~~~
		// 请注意:因为到这里   匹配上的可能还不止一个  所以才需要继续处理~~
		if (!matches.isEmpty()) {
			// getMappingComparator这个方法也是抽象方法由子类去实现的。
			// 比如:`RequestMappingInfoHandlerMapping`的实现为先比较Method,patterns、params
			Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
			matches.sort(comparator);
			// 排序后的最佳匹配为get(0)
			Match bestMatch = matches.get(0);
	
			// 如果总的匹配个数大于1的话
			if (matches.size() > 1) {
				if (CorsUtils.isPreFlightRequest(request)) {
					return PREFLIGHT_AMBIGUOUS_MATCH;
				}
		
				// 次最佳匹配
				Match secondBestMatch = matches.get(1);
				// 如果发现次最佳匹配和最佳匹配  比较是相等的  那就报错吧~~~~
				// Ambiguous handler methods mapped for~~~
				// 注意:这个是运行时的检查,在启动的时候是检查不出来的~~~  所以运行期的这个检查也是很有必要的~~~   否则就会出现意想不到的效果
				if (comparator.compare(bestMatch, secondBestMatch) == 0) {
					Method m1 = bestMatch.handlerMethod.getMethod();
					Method m2 = secondBestMatch.handlerMethod.getMethod();
					String uri = request.getRequestURI();
					throw new IllegalStateException(
							"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
				}
			}
			// 把最最佳匹配的方法  放进request的属性里面~~~
			request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
			// 它也是做了一件事:request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, lookupPath)
			handleMatch(bestMatch.mapping, lookupPath, request);
			// 最终返回的是HandlerMethod~~~
			return bestMatch.handlerMethod;
		}
		// 一个都没匹配上,handleNoMatch这个方法虽然不是抽象方法,protected方法子类复写
		// RequestMappingInfoHandlerMapping有复写此方法~~~~
		else {
			return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
		}
	}
	...

	// 因为上面说了mappings可能会有多个,比如get post put的都算~~~这里就是要进行筛选出所有match上的
	private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
		for (T mapping : mappings) {
			// 只有RequestMappingInfoHandlerMapping 实现了一句话:return info.getMatchingCondition(request);
			// 因此RequestMappingInfo#getMatchingCondition() 方法里大有文章可为~~~
			// 它会对所有的methods、params、headers... 都进行匹配  但凡匹配不上的就返回null  
			T match = getMatchingMapping(mapping, request);
			if (match != null) {
				matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
			}
		}
	}
}

// ===============RequestMappingInfo 的源码部分讲解================
public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {
	
	// 这些个匹配器都继承自AbstractRequestCondition,会进行各自的匹配工作  
	// 下面会以PatternsRequestCondition为例进行示例讲解~~~~~
	// 他们顶级抽象接口为:RequestCondition  @since 3.1 :Contract for request mapping conditions
	private final PatternsRequestCondition patternsCondition;
	private final RequestMethodsRequestCondition methodsCondition;
	private final ParamsRequestCondition paramsCondition;
	private final HeadersRequestCondition headersCondition;
	private final ConsumesRequestCondition consumesCondition;
	private final ProducesRequestCondition producesCondition;
	private final RequestConditionHolder customConditionHolder;

	// 因为类上和方法上都可能会有@RequestMapping注解,所以这里是把语意思合并  该方法来自顶层接口
	@Override
	public RequestMappingInfo combine(RequestMappingInfo other) {
		String name = combineNames(other);
		PatternsRequestCondition patterns = this.patternsCondition.combine(other.patternsCondition);
		RequestMethodsRequestCondition methods = this.methodsCondition.combine(other.methodsCondition);
		ParamsRequestCondition params = this.paramsCondition.combine(other.paramsCondition);
		HeadersRequestCondition headers = this.headersCondition.combine(other.headersCondition);
		ConsumesRequestCondition consumes = this.consumesCondition.combine(other.consumesCondition);
		ProducesRequestCondition produces = this.producesCondition.combine(other.producesCondition);
		RequestConditionHolder custom = this.customConditionHolder.combine(other.customConditionHolder);

		return new RequestMappingInfo(name, patterns,
				methods, params, headers, consumes, produces, custom.getCondition());
	}

	// 合并后,就开始发挥作用了,该接口来自于顶层接口~~~~
	@Override
	@Nullable
	public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
		RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
		if (methods == null) {
			return null;
		}
		ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
		if (params == null) {
			return null;
		}
		HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
		if (headers == null) {
			return null;
		}
		ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
		if (consumes == null) {
			return null;
		}
		ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);
		if (produces == null) {
			return null;
		}
		PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request);
		if (patterns == null) {
			return null;
		}
		RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);
		if (custom == null) {
			return null;
		}

		return new RequestMappingInfo(this.name, patterns,
				methods, params, headers, consumes, produces, custom.getCondition());
	}
}

​ 找到对应的handler之后根据handler找对应的HandlerAdapter, 我们使用的requestMapping对应的adapter是RequestMappingHandlerAdapter

​ 执行所有注册拦截器的preHandler方法, 该方法中调用所有拦截器的preHandle方法,如果preHandle返回true继续执行下一个拦截器 ,否则调用triggerAfterCompletion方法 , triggerAfterCompletion会倒叙调用已经执行过得拦截器的afterCompletion的方法。

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) {
			for (int i = 0; i < interceptors.length; i++) {
				HandlerInterceptor interceptor = interceptors[i];
				if (!interceptor.preHandle(request, response, this.handler)) {
					triggerAfterCompletion(request, response, null);
					return false;
				}
				this.interceptorIndex = i;
			}
		}
		return true;
	}
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
			throws Exception {

		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) {
			for (int i = this.interceptorIndex; i >= 0; i--) {
				HandlerInterceptor interceptor = interceptors[i];
				try {
					interceptor.afterCompletion(request, response, this.handler, ex);
				}
				catch (Throwable ex2) {
					logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
				}
			}
		}
	}

​ 看到这里,我们也就明白了拦截器的作用。

​ 执行完preHandle方法之后 , 下面到了比较核心的方法执行adapter的handle()方法, 方法中又调用了invokeHandlerMethod()方法

   protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
   			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        // handlerMethod 就是 RequestMapping注解的方法
   		ServletWebRequest webRequest = new ServletWebRequest(request, response);
   		try {
   		    // 获取WebDataBinderFactory,绑定request 和 参数(Java)之间,
   		    // 也就是一段url?date=20181011=>date = Sun Aug 19 00:00:00 CST 2018
   		    // 这种1:1的关系的时候,spring封装了一个对象,叫做WebDataBinder
   		    
   		    // 这里面做了两个事:
   		    // 1. 查询当前方法匹配的所有InitBinder方法,并且每个InitBinder注解对应的方法会被创建为InvocableHandlerMethod,并在hello方法之前调用
   		    // 2. 创建一个 ServletRequestDataBinderFactory
   			WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
   			ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
   
            // 创建一个Servlet的HandlerMethod
   			ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
   			if (this.argumentResolvers != null) {
   				invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
   			}
   			if (this.returnValueHandlers != null) {
   				invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
   			}
   			invocableMethod.setDataBinderFactory(binderFactory);
   			invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
   
   			ModelAndViewContainer mavContainer = new ModelAndViewContainer();
   			
   			// 在重定向之前从请求返回只读“输入”闪存属性
   			mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
   			// 按照以下的顺序填充model
   			// a. 检索@SessionAttributes上已经可以使用的会话属性。
            // b. 执行 @ModelAttribute 注释的方法 
            // c. 查找同样列为@SessionAttributes的@ModelAttribute方法参数,并确保它们出现在模型中,如果需要则引发异常
   			modelFactory.initModel(webRequest, mavContainer, invocableMethod);
   			// 是否在重定向的时候忽略默认的Model ;;;默认 false
   			mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
   
            // 创建一步的web请求
   			AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
   			asyncWebRequest.setTimeout(this.asyncRequestTimeout);
   
            // web请求管理器
   			WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
   			asyncManager.setTaskExecutor(this.taskExecutor);
   			asyncManager.setAsyncWebRequest(asyncWebRequest);
   			asyncManager.registerCallableInterceptors(this.callableInterceptors);
   			asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
   
            // 如果有并发结果
   			if (asyncManager.hasConcurrentResult()) {
   				Object result = asyncManager.getConcurrentResult();
   				mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
   				asyncManager.clearConcurrentResult();
   				LogFormatUtils.traceDebug(logger, traceOn -> {
   					String formatted = LogFormatUtils.formatValue(result, !traceOn);
   					return "Resume with async result [" + formatted + "]";
   				});
   				invocableMethod = invocableMethod.wrapConcurrentResult(result);
   			}
   			
   			// 执行方法,真正的开始执行,之前都是在为这个做准备
   			// 分为三步0.处理入参 1. 处理逻辑执行  2. 对返回值进行处理
   			invocableMethod.invokeAndHandle(webRequest, mavContainer);
   			if (asyncManager.isConcurrentHandlingStarted()) {
   				return null;
   			}
   
            // 
   			return getModelAndView(mavContainer, modelFactory, webRequest);
   		}
   		finally {
   			webRequest.requestCompleted();
   		}
   	}


public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
        // 执行...
		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
		setResponseStatus(webRequest);

		if (returnValue == null) {
			if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
				mavContainer.setRequestHandled(true);
				return;
			}
		}
		else if (StringUtils.hasText(getResponseStatusReason())) {
			mavContainer.setRequestHandled(true);
			return;
		}

		mavContainer.setRequestHandled(false);
		Assert.state(this.returnValueHandlers != null, "No return value handlers");
		try {
		    // 对返回值进行处理
		    			this.returnValueHandlers.handleReturnValue(
					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
		}
		catch (Exception ex) {
			if (logger.isTraceEnabled()) {
				logger.trace(formatErrorForReturnValue(returnValue), ex);
			}
			throw ex;
		}
	}
  @Nullable
	public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
         // 获取方法入参
		Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
		if (logger.isTraceEnabled()) {
			logger.trace("Arguments: " + Arrays.toString(args));
		}
		// HandlerMethod执行
		return doInvoke(args);
	}
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {

		if (ObjectUtils.isEmpty(getMethodParameters())) {
			return EMPTY_ARGS;
		}
		// 获取到当前方法的所有参数的元信息,如参数名、参数类型、参数的注解
		MethodParameter[] parameters = getMethodParameters();
		Object[] args = new Object[parameters.length];
		// 
		for (int i = 0; i < parameters.length; i++) {
			MethodParameter parameter = parameters[i];
			parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
			args[i] = findProvidedArgument(parameter, providedArgs);
			if (args[i] != null) {
				continue;
			}
			if (!this.resolvers.supportsParameter(parameter)) {
				throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
			}
			try {
			    // 处理参数
				args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
			}
			catch (Exception ex) {
				// Leave stack trace for later, exception may actually be resolved and handled..
				if (logger.isDebugEnabled()) {
					String error = ex.getMessage();
					if (error != null && !error.contains(parameter.getExecutable().toGenericString())) {
						logger.debug(formatArgumentError(parameter, error));
					}
				}
				throw ex;
			}
		}
		return args;
	}


可以看出,以上方法就是遍历处理入参,但是可以从这个代码中引出一个概念叫做:HandlerMethodArgumentResolver,HandlerMethod的参数解析器。
不禁联想到,在我们日常编写代码时,经常会从request里面拿一些内容,比如请求参数、请求头信息、请求体、session信息等,
spring采用注解的方式为我们提供了便捷,比如说是@RequestParam用来获取请求信息,@RequestBody用来获取请求体信息等
具体如下表:

注解作用处理器
RequestParam请求参数,如get请求、post请求RequestParamMethodArgumentResolver
SessionAttribute从session中获取值SessionAttributeMethodArgumentResolver
SessionStatusSession状态:判断session是否结束SessionStatusMethodArgumentResolver
PathVariable处理请求中路径信息PathVariableMethodArgumentResolver
还有其它的,如有兴趣。在下面这个包里:org.springframework.web.servlet.mvc.method.annotation
	我们知道,在Spring中绝大部分的用作处理一个流程的类里面,都是有一个抽象类在做公共的事情,对这个处理流程进行定义,当然这个方法参数处理器也不例外

原文链接:https://blog.youkuaiyun.com/u013076044/article/details/88086156

入参处理完成后执行invoke()方法,走真正的代码逻辑 , 调用完成之后 ,最后还要执行一个拦截器的postHandle()方法 , 最终进行一些视图的处理就完成了请求 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值