返回

Spring5 MVC——请求参数绑定到方法形参,执行方法处理请求

发布时间:2023-01-10 22:46:47 292
# spring# ios# json# 服务器# 数据

SpringMVC的核心流程

  • 建立请求和Controller方法的映射集合的流程。

  • 根据请求查找对应的Controller方法的流程。

  • 请求参数绑定到方法形参,执行方法处理请求,返回结果进行视图渲染的流程。

RequestMappingHandlerMapping继承类图

HandlerMapping

HandlerMapping接口作用是将请求映射到处理程序,以及预处理和处理后的拦截器列表,映射是基于一些标准的,其中的细节因不同的实现而不相同。这是官方文档上一段描述,该接口只有一个方法getHandler(request),返回一个HandlerExecutionChain对象,接口本身很简单。

public interface HandlerMapping {


    String BEST_MATCHING_HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingHandler";


    String LOOKUP_PATH = HandlerMapping.class.getName() + ".lookupPath";


    String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";


    String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";


    String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";


    String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";


    String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";


    String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";

    // 返回请求的一个处理程序handler和拦截器interceptors
    @Nullable
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

}

HandlerExecutionChain

HandlerExecutionChain 是Handler执行链,包含handler Object、interceptor。所以HandlerExecutionChain 提供了getHandler、getInterceptors方法,配置文件中配置的interceptor都会加入到HandlerExecutionChain。

public class HandlerExecutionChain {

    //handler实例
    private final Object handler;

    //一组拦截器实例
    @Nullable
    private HandlerInterceptor[] interceptors;

    //HandlerInterceptor的链表
    @Nullable
    private List interceptorList;

    ......
}

AbstractHandlerMapping

从类图中可知,HandlerMapping主要由AbstractHandlerMapping实现。

public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
        implements HandlerMapping, Ordered, BeanNameAware {

    @Override
    @Nullable
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        //获取handler的具体逻辑,留给子类实现
        Object handler = getHandlerInternal(request);
        // 如果获取到的handler为null,则采用默认的handler,即属性defaultHandler
        if (handler == null) {
            handler = getDefaultHandler();
        }
        if (handler == null) {
            //如果连DefaultHandler也为空,则直接返回空
            return null;
        }
        // Bean name or resolved handler?
        //如果handler是beanName,从容器里获取对应的bean
        if (handler instanceof String) {
            String handlerName = (String) handler;
            handler = obtainApplicationContext().getBean(handlerName);
        }

        //根据handler和request获取处理器链HandlerExecutionChain
        HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

        if (logger.isTraceEnabled()) {
            logger.trace("Mapped to " + handler);
        }
        else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
            logger.debug("Mapped to " + executionChain.getHandler());
        }

        if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
            CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
            CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
            config = (config != null ? config.combine(handlerConfig) : handlerConfig);
            executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
        }

        return executionChain;
    }
}

  • 用户发起请求,会进入到AbstractHandlerMapping类中的getHandler方法中。

  • 沿着方法的调用栈发现,请求先经过FrameworkServlet的service方法去处理。

  • 在由HttpServlet的service方法转发到FrameworkServlet的doPost方法中。

  • doPost方法中会调用FrameworkServlet的processRequest方法。

  • processRequest方法会调用DispatcherServlet的doService方法。

  • doService方法会调用DispatcherServlet的doDispatch方法。

  • doDispatch方法会调用到DispatcherServlet的getHandler方法。

  • DispatcherServlet的getHandler方法会调用到AbstractHandlerMapping中的getHandler方法。

DispatcherServlet#doDispatch

public class DispatcherServlet extends FrameworkServlet {

	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.
				// 根据当前request获取HandlerExecutionChain,
				// HandlerExecutionChain中包含了请求url,以及对应的controller以及controller中的方法
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				// 通过HandlerExecutionChain获取对应的适配器,adapter负责完成参数解析
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				//处理GET、HEAD请求的Last-modified
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}
				// 遍历所有定义的 interceptor,执行 preHandle 方法
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// Actually invoke the handler.
				// 调用目标Controller中的方法
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}
				// 处理成默认视图名,也就是添加前缀和后缀等
				applyDefaultViewName(processedRequest, mv);
				// 拦截器postHandle方法进行处理
				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);
				}
			}
		}
	}
}

DispatcherServlet#getHandlerAdapter

  • 通过HandlerExecutionChain获取对应的适配器,adapter负责完成参数解析。
public class DispatcherServlet extends FrameworkServlet {

	protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
		if (this.handlerAdapters != null) {
			// 遍历所有的 HandlerAdapter,找到和当前 Handler 匹配的HandlerAdapter实例
			for (HandlerAdapter adapter : this.handlerAdapters) {
				if (adapter.supports(handler)) {
					return adapter;
				}
			}
		}
		throw new ServletException("No adapter for handler [" + handler +
				"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
	}
}

该方法会去遍历事先注册好的HandlerAdapter实现类,去调用实现类的supports方法判断是否支持当前的handler实例的处理,一旦找到匹配的HandlerAdapter实现类,直接返回HandlerAdapter实例。

通过方法调用栈发现,HandlerAdapter初始化的时候,,一共向List handlerAdapters集合中注册了4个HandlerAdapter的实现类:

  • requestMappingHandlerAdapter -> RequestMappingHandlerAdapter

  • handlerFunctionAdapter -> HandlerFunctionAdapter

  • httpRequestHandlerAdapter -> HttpRequestHandlerAdapter

  • simpleControllerHandlerAdapter -> SimpleControllerHandlerAdapter

这里主要关注RequestMappingHandlerAdapter

supports

public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {

	@Override
	public final boolean supports(Object handler) {
		return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
	}
}

supports方法入参为RequestMappingHandlerAdapter 时,满足条件,直接返回该实例

接着将实现拉回doDispatch方法中

HandlerExecutionChain#applyPreHandle

  • 拦截器执行拦截,对客户端请求requset进行拦截,一旦某个HandlerInterceptor的preHandle方法返回false,则直接return,不在对请求在进行处理。
// 遍历所有定义的 interceptor,执行 preHandle 方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
	return;
}

public class HandlerExecutionChain {

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

如果HandlerExecutionChain#applyPreHandle方法返回true,则执行HandlerAdapter的handle方法执行请求。

HandlerAdapter#handle

  • 调用目标Controller中的方法
public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {

	@Override
	@Nullable
	public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		return handleInternal(request, response, (HandlerMethod) handler);
	}
}

该方法的返回值是ModelAndView实例。

RequestMappingHandlerAdapter#handleInternal

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
		implements BeanFactoryAware, InitializingBean {

	@Override
	protected ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ModelAndView mav;
		// 1.检测当前请求,验证请求方法合法性和session合法性
		checkRequest(request);

		// Execute invokeHandlerMethod in synchronized block if required.
		// 2.根据synchronizeOnSession值判断,当前请求是否需串行化访问。
		if (this.synchronizeOnSession) {
			HttpSession session = request.getSession(false);
			if (session != null) {
				// 获取最佳互斥锁,即同步当前回话对象;如未能获取到互斥锁,将返回HttpSession对象本身
				Object mutex = WebUtils.getSessionMutex(session);
				synchronized (mutex) {
					mav = invokeHandlerMethod(request, response, handlerMethod);
				}
			}
			else {
				// No HttpSession available -> no mutex necessary
				// 即无最佳互斥锁,也未能获取到HttpSession,则当前会话无需串行化访问
				mav = invokeHandlerMethod(request, response, handlerMethod);
			}
		}
		else {
			// No synchronization on session demanded at all...
			//当前会话无需串行化访问
			mav = invokeHandlerMethod(request, response, handlerMethod);
		}
		// 3.相应信息不包含Cache-Control
		if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
			if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
				applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
			}
			else {
				prepareResponse(response);
			}
		}

		return mav;
	}
}

invokeHandlerMethod

  • 具体的处理方法委托给了invokeHandlerMethod方法
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
		implements BeanFactoryAware, InitializingBean {

	@Nullable
	protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		try {
			//WebDataBinderFactory --> 工厂类,为目标对象创建一个WebDataBinder实例
			// 1.WebDataBinder继承了DataBinder类,为web请求提供了参数绑定服务
			WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);

			// 获取ModelFactory:
			// 2.ModelFactory可以协助控制器在调用方法之前初始化模型,并在调用之后更新模型
			ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

			// 创建ServletInvocableHandlerMethod对象
			// 3.ServletInvocableHandlerMethod继承并扩展了InvocableHandlerMethod
			ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);

			// 4.尝试绑定参数、返回值解析器
			if (this.argumentResolvers != null) {
				//设置参数解析器
				invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
			}
			if (this.returnValueHandlers != null) {
				//设置spring mvc请求controller的method返回值处理器
				invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
			}
			invocableMethod.setDataBinderFactory(binderFactory);
			//设置参数名称发现器
			invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

			// 5.创建ModelAndViewContainer,并初始化Model对象
			ModelAndViewContainer mavContainer = new ModelAndViewContainer();
			mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
			modelFactory.initModel(webRequest, mavContainer, invocableMethod);
			mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

			// 6.异步请求相关
			AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
			asyncWebRequest.setTimeout(this.asyncRequestTimeout);

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

			// 7.调用Controller中的具体方法并处理返回值
			//核心逻辑,最终去执行Handler方法处理请求
			invocableMethod.invokeAndHandle(webRequest, mavContainer);
			if (asyncManager.isConcurrentHandlingStarted()) {
				return null;
			}

			// 8.返回ModelAndView对象
			return getModelAndView(mavContainer, modelFactory, webRequest);
		}
		finally {
			// 完成请求后续处理,并将当前请求置为未激活
			webRequest.requestCompleted();
		}
	}
}

invokeHandlerMethod方法还是很复杂的,下面我们对该方法进行详细的分析。。。

getDataBinderFactory

  • WebDataBinderFactory --> 工厂类,为目标对象创建一个WebDataBinder实例。
  • WebDataBinder继承了DataBinder类,为web请求提供了参数绑定服务。
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
		implements BeanFactoryAware, InitializingBean {

	private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
		// 1.获取handlerType,即目标类
		Class handlerType = handlerMethod.getBeanType();
		// 2.优先尝试从缓存中获取对应的InitBinder方法
		Set methods = this.initBinderCache.get(handlerType);
		// 如未能从缓存中获取,则根据handlerType对应的类,去类中查找所有标注了@InitBinder注解的方法,并将其缓存
		if (methods == null) {
			methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
			this.initBinderCache.put(handlerType, methods);
		}
		// 3.从标注了@ControllerAdvice类中寻找InitBinder方法,并优先为其创建InvocableHandlerMethod对象
		List initBinderMethods = new ArrayList<>();
		// Global methods first
		this.initBinderAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
			if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
				Object bean = controllerAdviceBean.resolveBean();
				for (Method method : methodSet) {
					initBinderMethods.add(createInitBinderMethod(bean, method));
				}
			}
		});
		// 4.为普通的InitBinder创建InvocableHandlerMethod对象
		for (Method method : methods) {
			Object bean = handlerMethod.getBean();
			initBinderMethods.add(createInitBinderMethod(bean, method));
		}
		// 5.创建InitBinderDataBinderFactory对象
		return createDataBinderFactory(initBinderMethods);
	}
}

该方法的处理流程很简单,但是这里涉及到两个点,普通的@InitBinder注解和标注了@ControllerAdvice注解类中的@InitBinder注解。从代码中可以看到,@ControllerAdvice注解中的InitBinder方法,是作为全局方法优先创建的。而普通的InitBinder方法只能应用于其类本身,无法作为全局对象应用到所有的Controller方法。

getModelFactory

  • 获取ModelFactory:ModelFactory可以协助控制器在调用方法之前初始化模型,并在调用之后更新模型。
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
		implements BeanFactoryAware, InitializingBean {

	private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
		// 1.处理@SessionAttributes注解
		SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
		Class handlerType = handlerMethod.getBeanType();
		// 2.处理@ModelAttribute注解
		Set methods = this.modelAttributeCache.get(handlerType);
		if (methods == null) {
			methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
			this.modelAttributeCache.put(handlerType, methods);
		}
		List attrMethods = new ArrayList<>();
		// Global methods first
		// 3.优先处理全局@ModelAttribute注解的方法,例如被@ControllerAdvice标注的类中存在被@ModelAttribute注解的方法,则优先处理
		this.modelAttributeAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
			if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
				Object bean = controllerAdviceBean.resolveBean();
				for (Method method : methodSet) {
					attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
				}
			}
		});
		// 4.循环所有标注了@ModelAttribute注解的方法,并创建InvocableHandlerMethod对象
		// InvocableHandlerMethod:负责具体的HandlerMethod的调用、参数解析等工作
		for (Method method : methods) {
			Object bean = handlerMethod.getBean();
			attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
		}
		// 5.返回ModelFactory对象
		// ModelFactory:协助在控制器方法调用之前初始化模型,并在调用之后更新它。
		return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
	}
}

该方法主要作用是处理@ModelAttribute和@SessionAttributes两个注解。关于两者的作用,不多赘述了!在最后创建了ModelFactory对象,该对象的作用可以协助在控制器方法调用之前初始化模型,并在调用之后更新它。

ModelFactory的initModel初始化

  • modelFactory.initModel(webRequest, mavContainer, invocableMethod)
public final class ModelFactory {

	public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod)
			throws Exception {
		// 1.解析并合并@SessionAttributes注解
		Map sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
		container.mergeAttributes(sessionAttributes);
		// 2.调用被@ModelAttribute注解的方法
		invokeModelAttributeMethods(request, container);
		// 3.查找标注了@ModelAttribute、@SessionAttributes的方法参数,确保其解析过程中不会发生异常
		for (String name : findSessionAttributeArguments(handlerMethod)) {
			if (!container.containsAttribute(name)) {
				Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
				if (value == null) {
					throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
				}
				container.addAttribute(name, value);
			}
		}
	}
}

注意这里会有一个Expected session attribute xxx的异常,如果类上标注了@SessionAttributes注解,且在方法中标注了@ModelAttribute注解,如果@ModelAttribute为空,则会抛出此异常。。。

ServletInvocableHandlerMethod#invokeAndHandle

  • 核心逻辑,最终去执行Handler方法处理请求
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {

	public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
		//在这里通过反射调用controller中的method方法
		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);

		// 2.设置返回状态码
		setResponseStatus(webRequest);

		// 3.当前请求无返回值或者返回值中包含错误,则将请求完成标识设置为true并返回
		if (returnValue == null) {
			if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
				disableContentCachingIfNecessary(webRequest);
				mavContainer.setRequestHandled(true);
				return;
			}
		}
		else if (StringUtils.hasText(getResponseStatusReason())) {
			mavContainer.setRequestHandled(true);
			return;
		}

		// 4.当前请求有返回值且无错误信息,则将请求完成标识设置为false,并继续处理当前请求
		mavContainer.setRequestHandled(false);
		Assert.state(this.returnValueHandlers != null, "No return value handlers");
		try {
			// 选取合适的HandlerMethodReturnValueHandler,并处理返回值
			//在这里通过返回值处理器处理器进行二次处理,
			// 例如:如果加了方法加了@ReponseBody注解,就把结果序列化json格式再返回
			this.returnValueHandlers.handleReturnValue(
					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
		}
		catch (Exception ex) {
			if (logger.isTraceEnabled()) {
				logger.trace(formatErrorForReturnValue(returnValue), ex);
			}
			throw ex;
		}
	}
}

InvocableHandlerMethod#invokeForRequest

  • 通过反射调用controller中的method方法
public class InvocableHandlerMethod extends HandlerMethod {

	@Nullable
	public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {

		//将请求的参数值绑定到Controller方法里
		Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
		if (logger.isTraceEnabled()) {
			logger.trace("Arguments: " + Arrays.toString(args));
		}
		return doInvoke(args);
	}
}

InvocableHandlerMethod#getMethodArgumentValues

  • 将请求的参数值绑定到Controller方法里
public class InvocableHandlerMethod extends HandlerMethod {

	protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
		//获取Controller方法实例里面的参数列表
		MethodParameter[] parameters = getMethodParameters();
		if (ObjectUtils.isEmpty(parameters)) {
			return EMPTY_ARGS;
		}
		//创建一个参数数组,保存从request解析出的方法参数
		Object[] args = new Object[parameters.length];
		for (int i = 0; i < parameters.length; i++) {
			MethodParameter parameter = parameters[i];
			//给每一个Controller方法实例参数初始化一个参数名称发现器
			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 exMsg = ex.getMessage();
					if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
						logger.debug(formatArgumentError(parameter, exMsg));
					}
				}
				throw ex;
			}
		}
		return args;
	}
}

resolveArgument

  • 解析并绑定参数的核心逻辑
public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {

	@Override
	@Nullable
	public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
		//首先获取参数解析器,这里获取的逻辑是首先从argumentResolverCache缓存中
		// 获取该MethodParameter匹配的HandlerMethodArgumentResolver。
		// 如果为空,遍历初始化定义的那26个解析器实例,
		// 查找匹配的HandlerMethodArgumentResolver,然后添加至argumentResolverCache缓存中
		HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
		if (resolver == null) {
			throw new IllegalArgumentException("Unsupported parameter type [" +
					parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
		}
		// 解析参数,不同的参数解析器实例,有不同的解析方式
		return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
	}
}

HandlerMethodArgumentResolverComposite#getArgumentResolver

  • 获取和参数相匹配的解析器
  • 首先获取参数解析器,这里获取的逻辑是首先从argumentResolverCache缓存中
  • 获取该MethodParameter匹配的HandlerMethodArgumentResolver。
  • 如果为空,遍历初始化定义的那26个解析器实例,
  • 查找匹配的HandlerMethodArgumentResolver,然后添加至argumentResolverCache缓存中。
public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {

	@Nullable
	private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
		HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
		if (result == null) {
			for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
				if (resolver.supportsParameter(parameter)) {
					result = resolver;
					this.argumentResolverCache.put(parameter, result);
					break;
				}
			}
		}
		return result;
	}
}

AbstractNamedValueMethodArgumentResolver#resolveArgument

  • 解析参数,不同的参数解析器实例,有不同的解析方式
public abstract class AbstractNamedValueMethodArgumentResolver implements HandlerMethodArgumentResolver {

	@Override
	@Nullable
	public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
		//从@RequestParam里获取到对应参数名
		// 1.NamedValueInfo对象包含了name,required,defaultValue三个信息
		NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
		// 获取MethodParameter对象,该对象封装了方法参数的规范
		MethodParameter nestedParameter = parameter.nestedIfOptional();

		// 2.解析参数名,包括占位符和表达式等
		Object resolvedName = resolveStringValue(namedValueInfo.name);
		if (resolvedName == null) {
			throw new IllegalArgumentException(
					"Specified name must not resolve to null: [" + namedValueInfo.name + "]");
		}
		//和请求里面的参数名进行对照,获取请求参数对应的String类型的值
		// 3.将给定的参数类型和值名称解析为参数值。
		Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
		if (arg == null) {
			// 如果默认值不为空,则
			if (namedValueInfo.defaultValue != null) {
				arg = resolveStringValue(namedValueInfo.defaultValue);
			}
			// 指定了required属性且该参数不是为非不必须,则调动handleMissingValue方法处理缺失值,该方法一般会抛出异常
			else if (namedValueInfo.required && !nestedParameter.isOptional()) {
				handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
			}
			// 最后处理将该参数值处理为null即可
			arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
		}
		else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
			arg = resolveStringValue(namedValueInfo.defaultValue);
		}

		if (binderFactory != null) {
			// 4.创建WebDataBinder实例
			WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
			try {
				//参数类型转换
				arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
			}
			catch (ConversionNotSupportedException ex) {
				throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
						namedValueInfo.name, parameter, ex.getCause());
			}
			catch (TypeMismatchException ex) {
				throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
						namedValueInfo.name, parameter, ex.getCause());
			}
		}

		handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);

		return arg;
	}
}

RequestParamMethodArgumentResolver#resolveName

  • 和请求里面的参数名进行对照,获取请求参数对应的String类型的值
public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver
		implements UriComponentsContributor {

	@Override
	@Nullable
	protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
		HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);

		if (servletRequest != null) {
			Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
			if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {
				return mpArg;
			}
		}

		Object arg = null;
		MultipartRequest multipartRequest = request.getNativeRequest(MultipartRequest.class);
		if (multipartRequest != null) {
			List files = multipartRequest.getFiles(name);
			if (!files.isEmpty()) {
				arg = (files.size() == 1 ? files.get(0) : files);
			}
		}
		if (arg == null) {
			//通过参数名尝试从请求里获取对应的值
			String[] paramValues = request.getParameterValues(name);
			if (paramValues != null) {
				arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
			}
		}
		return arg;
	}
}

convertIfNecessary

  • 参数类型转换
public class DataBinder implements PropertyEditorRegistry, TypeConverter {

	@Override
	@Nullable
	public  T convertIfNecessary(@Nullable Object value, @Nullable Class requiredType,
			@Nullable MethodParameter methodParam) throws TypeMismatchException {

		return getTypeConverter().convertIfNecessary(value, requiredType, methodParam);
	}

}


public abstract class TypeConverterSupport extends PropertyEditorRegistrySupport implements TypeConverter {

	@Override
	@Nullable
	public  T convertIfNecessary(@Nullable Object value, @Nullable Class requiredType,
			@Nullable MethodParameter methodParam) throws TypeMismatchException {

		return convertIfNecessary(value, requiredType,
				(methodParam != null ? new TypeDescriptor(methodParam) : TypeDescriptor.valueOf(requiredType)));
	}

	@Nullable
	@Override
	public  T convertIfNecessary(@Nullable Object value, @Nullable Class requiredType,
			@Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException {

		Assert.state(this.typeConverterDelegate != null, "No TypeConverterDelegate");
		try {
			return this.typeConverterDelegate.convertIfNecessary(null, null, value, requiredType, typeDescriptor);
		}
		catch (ConverterNotFoundException | IllegalStateException ex) {
			throw new ConversionNotSupportedException(value, requiredType, ex);
		}
		catch (ConversionException | IllegalArgumentException ex) {
			throw new TypeMismatchException(value, requiredType, ex);
		}
	}
}


class TypeConverterDelegate {
	@Nullable
	public  T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue,
			@Nullable Class requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {

		// Custom editor for this type?
		// 1、判断有无自定义属性编辑器
		PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);

		ConversionFailedException conversionAttemptEx = null;

		// No custom editor but custom ConversionService specified?
		// 2、判断有无自定义ConversionService
		ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
		if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
			TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
			if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
				try {
					return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
				}
				catch (ConversionFailedException ex) {
					// fallback to default conversion logic below
					conversionAttemptEx = ex;
				}
			}
		}

		Object convertedValue = newValue;

		// Value not of required type?
		// ClassUtils.isAssignableValue(requiredType, convertedValue)-->判断requiredType和convertedValue的class,是否相同,
		// 相同返回->true;否则返回->false
		// 3、 如果有自定义属性编辑器或者通过解析出来的值类型与真实的值类型的class不同
		// 例如,我们需要将value转换成int时
		if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
			if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) &&
					convertedValue instanceof String) {
				TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor();
				if (elementTypeDesc != null) {
					Class elementType = elementTypeDesc.getType();
					if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) {
						convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
					}
				}
			}
			if (editor == null) {
				editor = findDefaultEditor(requiredType);
			}
			convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
		}

		boolean standardConversion = false;

		// 4、执行转换
		if (requiredType != null) {
			// Try to apply some standard type conversion rules if appropriate.

			if (convertedValue != null) {
				// Object类型
				if (Object.class == requiredType) {
					return (T) convertedValue;
				}
				// 数组类型
				else if (requiredType.isArray()) {
					// Array required -> apply appropriate conversion of elements.
					if (convertedValue instanceof String && Enum.class.isAssignableFrom(requiredType.getComponentType())) {
						convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
					}
					return (T) convertToTypedArray(convertedValue, propertyName, requiredType.getComponentType());
				}
				// 集合类型
				else if (convertedValue instanceof Collection) {
					// Convert elements to target type, if determined.
					convertedValue = convertToTypedCollection(
							(Collection) convertedValue, propertyName, requiredType, typeDescriptor);
					standardConversion = true;
				}
				// map类型
				else if (convertedValue instanceof Map) {
					// Convert keys and values to respective target type, if determined.
					convertedValue = convertToTypedMap(
							(Map) convertedValue, propertyName, requiredType, typeDescriptor);
					standardConversion = true;
				}

				// 注意:这里是新开启的if,不接上面的else if
				// 如果经过转换过的值是数组类型,且其长度只有1,那么只取其第0个作为最终转换值
				if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) {
					convertedValue = Array.get(convertedValue, 0);
					standardConversion = true;
				}

				// 如果类型是String,并且是java的基本数据类型或者包装类型
				// 包括 boolean, byte, char, short, int, long, float, double
				// 和 Boolean, Byte, Character, Short, Integer, Long, Float, Double
				// 那么直接调用toString()方法返回即可,注意convertedValue是Object类型,不是基本或包装类型,所以是可以调用toString()方法的
				if (String.class == requiredType && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {
					// We can stringify any primitive value...
					return (T) convertedValue.toString();
				}
				// 如果转换值是String类的实例,但是我们又不能转换为解析出来的requiredType的实例
				// 例如枚举类型值的注入
				else if (convertedValue instanceof String && !requiredType.isInstance(convertedValue)) {
					if (conversionAttemptEx == null && !requiredType.isInterface() && !requiredType.isEnum()) {
						try {
							Constructor strCtor = requiredType.getConstructor(String.class);
							return BeanUtils.instantiateClass(strCtor, convertedValue);
						}
						// 删除logger信息
						catch (NoSuchMethodException ex) {
							// proceed with field lookup
							if (logger.isTraceEnabled()) {
								logger.trace("No String constructor found on type [" + requiredType.getName() + "]", ex);
							}
						}
						catch (Exception ex) {
							if (logger.isDebugEnabled()) {
								logger.debug("Construction via String failed for type [" + requiredType.getName() + "]", ex);
							}
						}
					}
					String trimmedValue = ((String) convertedValue).trim();
					if (requiredType.isEnum() && trimmedValue.isEmpty()) {
						// It's an empty enum identifier: reset the enum value to null.
						return null;
					}
					convertedValue = attemptToConvertStringToEnum(requiredType, trimmedValue, convertedValue);
					standardConversion = true;
				}
				// 数值类型
				else if (convertedValue instanceof Number && Number.class.isAssignableFrom(requiredType)) {
					convertedValue = NumberUtils.convertNumberToTargetClass(
							(Number) convertedValue, (Class) requiredType);
					standardConversion = true;
				}
			}
			else {
				// convertedValue == null
				if (requiredType == Optional.class) {
					convertedValue = Optional.empty();
				}
			}

			// 5、 判定requiredType是否可从convertedValue转换而来,并尝试使用conversionService转换,及处理转换异常
			if (!ClassUtils.isAssignableValue(requiredType, convertedValue)) {
				if (conversionAttemptEx != null) {
					// Original exception from former ConversionService call above...
					throw conversionAttemptEx;
				}
				else if (conversionService != null && typeDescriptor != null) {
					// ConversionService not tried before, probably custom editor found
					// but editor couldn't produce the required type...
					TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
					if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
						return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
					}
				}

				// Definitely doesn't match: throw IllegalArgumentException/IllegalStateException
				// 到此为止,可以确定类型不匹配,无法转换,抛出IllegalArgumentException/IllegalStateException
				StringBuilder msg = new StringBuilder();
				msg.append("Cannot convert value of type '").append(ClassUtils.getDescriptiveType(newValue));
				msg.append("' to required type '").append(ClassUtils.getQualifiedName(requiredType)).append("'");
				if (propertyName != null) {
					msg.append(" for property '").append(propertyName).append("'");
				}
				if (editor != null) {
					msg.append(": PropertyEditor [").append(editor.getClass().getName()).append(
							"] returned inappropriate value of type '").append(
							ClassUtils.getDescriptiveType(convertedValue)).append("'");
					throw new IllegalArgumentException(msg.toString());
				}
				else {
					msg.append(": no matching editors or conversion strategy found");
					throw new IllegalStateException(msg.toString());
				}
			}
		}

		if (conversionAttemptEx != null) {
			if (editor == null && !standardConversion && requiredType != null && Object.class != requiredType) {
				throw conversionAttemptEx;
			}
			logger.debug("Original ConversionService attempt failed - ignored since " +
					"PropertyEditor based conversion eventually succeeded", conversionAttemptEx);
		}
		// 6、返回转换值
		return (T) convertedValue;
	}
}

doInvoke方法调用

public class InvocableHandlerMethod extends HandlerMethod {

	protected Object doInvoke(Object... args) throws Exception {
		ReflectionUtils.makeAccessible(getBridgedMethod());
		try {
			return getBridgedMethod().invoke(getBean(), args);
		}
		catch (IllegalArgumentException ex) {
			assertTargetBean(getBridgedMethod(), getBean(), args);
			String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
			throw new IllegalStateException(formatInvokeError(text, args), ex);
		}
		catch (InvocationTargetException ex) {
			// Unwrap for HandlerExceptionResolvers ...
			Throwable targetException = ex.getTargetException();
			if (targetException instanceof RuntimeException) {
				throw (RuntimeException) targetException;
			}
			else if (targetException instanceof Error) {
				throw (Error) targetException;
			}
			else if (targetException instanceof Exception) {
				throw (Exception) targetException;
			}
			else {
				throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
			}
		}
	}
}

setResponseStatus

  • 设置相应状态码以及handleReturnValue处理返回值
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {

	private void setResponseStatus(ServletWebRequest webRequest) throws IOException {
		// 获取HttpStatus
		HttpStatus status = getResponseStatus();
		// 未发现HttpStatus直接返回
		if (status == null) {
			return;
		}

		HttpServletResponse response = webRequest.getResponse();
		if (response != null) {
			String reason = getResponseStatusReason();
			if (StringUtils.hasText(reason)) {
				/**
				 * 注意 注意 注意:这里是 sendError , 不是 setError
				 * 使用指定的状态码并清空缓冲,发送一个错误响应至客户端。如果响应已经被提交,这个方法会抛出IllegalStateException。
				 * 服务器默认会创建一个HTML格式的服务错误页面作为响应结果,其中包含参数msg指定的文本信息,
				 * 这个HTML页面的内容类型为“text/html”,保留cookies和其他未修改的响应头信息。
				 *
				 * 如果一个对应于传入的错误码的错误页面已经在web.xml中声明,那么这个声明的错误页面将会优先于建议的msg参数服务于客户端。
				 */
				response.sendError(status.value(), reason);
			}
			else {
				/**
				 * 设置响应的状态码。
				 * 这个方法被用于当响应结果正常时(例如,状态码为SC_OK或SC_MOVED_TEMPORARTLY)设置响应状态码。
				 * 如果发生错误,而且来访者希望调用在web应用中定义的错误页面作为显示,那么应该使用sendError方法代替之。
				 * 使用setStatus方法之后,容器会清空缓冲并设置Location响应头,保留cookies和其他响应头信息。
				 */
				response.setStatus(status.value());
			}
		}

		// To be picked up by RedirectView
		webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, status);
	}
}

如果@RequestMapping设置了@ResponseStatus注解,这里则要根据注解设置放回状态码。如果getResponseStatusReason方法返回了错误信息,则直接通过sendError方法返回给前端。否则将状态码信息设置到response里即可。

 

在后续处理根据@RequestMapping方法返回值、相应信息等判断,是否将当前请求设置为已经完成。例如当前请求无需返回视图、或者当前请求的放回状态码包含了错误信息,则无需继续后续处理。

 

假设当前是有视图或者返回值,接下来应该选取合适的HandlerMethodReturnValueHandler并处理返回值,先来看一下HandlerMethodReturnValueHandler的定义:

public interface HandlerMethodReturnValueHandler {

	/**
	 * 判断当前策略(Handler)是否支持MethodParameter(方法返回类型)
	 */
	boolean supportsReturnType(MethodParameter returnType);

	/**
	 * 处理返回值,为模型添加属性、视图等想关内容
	 */
	void handleReturnValue(@Nullable Object returnValue,
						   MethodParameter returnType,
						   ModelAndViewContainer mavContainer,
						   NativeWebRequest webRequest) throws Exception;
}

getModelAndView方法后续处理

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
		implements BeanFactoryAware, InitializingBean {

	private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
			ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
		// 1.更新模型
		modelFactory.updateModel(webRequest, mavContainer);
		if (mavContainer.isRequestHandled()) {
			return null;
		}
		// 2.获取ModelMap并创建ModelAndView
		ModelMap model = mavContainer.getModel();
		ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
		// 3.处理引用类型视图和转发类型视图
		if (!mavContainer.isViewReference()) {
			mav.setView((View) mavContainer.getView());
		}
		if (model instanceof RedirectAttributes) {
			Map flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
			HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
			if (request != null) {
				RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
			}
		}
		return mav;
	}
}

实现拉回到doDispatch方法里面:

  • 执行处理成默认视图名,也就是添加前缀和后缀等。
  • 拦截器postHandle方法进行处理
  • 处理最后的结果,渲染之类的逻辑都在这里
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
	
	......

	try {
			......

			// 处理成默认视图名,也就是添加前缀和后缀等
			applyDefaultViewName(processedRequest, mv);
			// 拦截器postHandle方法进行处理
			mappedHandler.applyPostHandle(processedRequest, response, mv);
		}

		......

		// 处理最后的结果,渲染之类的逻辑都在这里
		processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
	}

	......

}

由于现在流行前后端分离,前后端通过json格式进行数据交互,所以视图渲染这个就不做过多阐述。

 

到此,请求参数绑定到方法形参,执行方法处理请求,返回结果进行视图渲染的流程就梳理完毕了。

 

参考: https://blog.csdn.net/lyc_liyanchao/article/details/87925667

https://blog.csdn.net/lyc_liyanchao/article/details/88085797

特别声明:以上内容(图片及文字)均为互联网收集或者用户上传发布,本站仅提供信息存储服务!如有侵权或有涉及法律问题请联系我们。
举报
评论区(0)
按点赞数排序
用户头像
精选文章
thumb 中国研究员首次曝光美国国安局顶级后门—“方程式组织”
thumb 俄乌线上战争,网络攻击弥漫着数字硝烟
thumb 从网络安全角度了解俄罗斯入侵乌克兰的相关事件时间线