之前我们讲到了通过请求找到了对应的映射器,也就是找到了对应的方法,那么如何调用这个方法呢,因为不同的接收请求方法有不同的方式,最简单的就是方法名就不一样,那么如何调用,这里采用了设计模式中的适配器模式,通过适配,进行判断,然后通过不同的方式调用方法,这就是HandlerAdpater 的主要功能
之前介绍了HandlerMapping,我们通过请求找到了对应的handler,那么我们继续来看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);
// Determine handler for the current request.
// 将我们的请求和逻辑方法映射起来
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
// 如果没有找到映射器 代表没有找到接口方法 设置返回404
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 = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
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);
}
}
}
}
我们先看没有找到对应的Handler
的对象,如果没有找到Handler对象的话,就执行noHandlerFound(processedRequest, response);
的方法,具体的代码如下:
protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 日志打印
if (pageNotFoundLogger.isWarnEnabled()) {
pageNotFoundLogger.warn("No mapping for " + request.getMethod() + " " + getRequestUri(request));
}
// 是否出现了异常
if (this.throwExceptionIfNoHandlerFound) {
throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request),
new ServletServerHttpRequest(request).getHeaders());
}
else {
// 没有出现异常 直接返回404
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
上面的代码其实很简单,判断是否需要打印日志,如果需要,就直接打印日志,如果在查找Handler
的时候出错了,就直接抛出异常,如果没有出现异常,就直接将状态码设置成404,然后返回。
然后就是根据对应的Handler
获取对应的HandlerAdapter
。这个时候会调用getHandlerAdapter(mappedHandler.getHandler());
方法,具体的代码如下:
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
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
,下面会先介绍一下HandlerAdapter
这个接口和其常用子类
该接口中有3个方法
public interface HandlerAdapter {
// 判断该适配器是否支持传入的handler
boolean supports(Object handler);
// 使用给定的handler来处理当前的request请求,调用对应的逻辑方法
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
// 返回handler最后修改的时间 该方法已经标记为废弃
@Deprecated
long getLastModified(HttpServletRequest request, Object handler);
}
HandlerAdapter 系统出提供了一些子类,但是我们也可以对其进行扩展,例如Spring Boot就对其就行了扩展
HttpRequestHandlerAdapter
SimpleControllerHandlerAdapter
RequestMappingHandlerAdapter
RequestMappingHandlerAdapter
HandlerAdapter
和 之前介绍的 HandlerMapping
初始化思路是一样,调用链:
org.springframework.web.servlet.HttpServletBean#init()
--> org.springframework.web.servlet.FrameworkServlet#initServletBean()
--> org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext()
--> org.springframework.web.servlet.DispatcherServlet#onRefresh()
--> org.springframework.web.servlet.DispatcherServlet#initStrategies()
--> org.springframework.web.servlet.DispatcherServlet#initHandlerAdapters()
通过上面的调用链,我们会调用初始化处理适配器方法 initHandlerAdapters
private void initHandlerAdapters(ApplicationContext context) {
this.handlerAdapters = null;
if (this.detectAllHandlerAdapters) {
// Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
// 从spring的容器中取,如果这儿加了@EnableWebMvc注解,这儿取出来的就是三个
Map<String, HandlerAdapter> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerAdapters = new ArrayList<>(matchingBeans.values());
// We keep HandlerAdapters in sorted order.
// 如果不为空,我们进行处理排序,因为适配器有匹配顺序
AnnotationAwareOrderComparator.sort(this.handlerAdapters);
}
}
else {
try {
HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
this.handlerAdapters = Collections.singletonList(ha);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerAdapter later.
}
}
// Ensure we have at least some HandlerAdapters, by registering
// default HandlerAdapters if no other adapters are found.
// 如果上面没有从Spring中获取不到适配器,那么就从配置文件中去默认的
if (this.handlerAdapters == null) {
this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerAdapters declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
上面的代码就是如果加了@EnableWebMvc
注解的话,就直接能从spring容器中取出对应的三个HandlerMapping
,如果没有加,则调用 getDefaultStrategies()
方法,
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
if (defaultStrategies == null) {
try {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
// 从默认的配置中 ‘DispatcherServlet.properties’ 取值
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
// 加载属性
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
}
}
// 遍历默认的映射器注入
String key = strategyInterface.getName();
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<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
Object strategy = createDefaultStrategy(context, clazz);
strategies.add((T) strategy);
}
catch (ClassNotFoundException ex) {
throw new BeanInitializationException(
"Could not find DispatcherServlet's default strategy class [" + className +
"] for interface [" + key + "]", ex);
}
catch (LinkageError err) {
throw new BeanInitializationException(
"Unresolvable class definition for DispatcherServlet's default strategy class [" +
className + "] for interface [" + key + "]", err);
}
}
return strategies;
}
else {
return Collections.emptyList();
}
}
上面的代码说如果spring容器中取不到,那么会从默认的配置文件中取,这个配置在通过IDEA中查看jar包 spring-webmvc-5.3.9.jar
,打开可以看到其中的一段配置
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,\
org.springframework.web.servlet.function.support.HandlerFunctionAdapter
上面我们通过doDispatch()
方法调用到了 HandlerAdapter
,上面我们介绍了这个接口及其子类,那么接下来我们按照类图来介绍一下接口
我们先看一下这个类的结构,看看有没有利用spring的一些扩展点,来完成一些数据的初始化的工作,具体如下:
可以发现这个类没有实现spring的扩展点,就是实现了HandlerAdapter
这个接口
public class HttpRequestHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
// 判断这个handler是否实现了HttpRequestHandler接口
return (handler instanceof HttpRequestHandler);
}
@Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 这里只是把handler强转一下,然后调用对应的handleRequest()方法
((HttpRequestHandler) handler).handleRequest(request, response);
return null;
}
// getLastModified() 方法已经标记废弃,这里省略
}
我们可以从上面的内容中看到,这个类很简单,调用supports
方法来进行适配,这个方法也只是进行了强转判断,并且在适配调用逻辑方法的适合,也很好理解,
测试代码
@Component("/name")
public class BeanNameController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("实现Controller接口方式");
return null;
}
}
在 getHandlerAdapter()
这个方法里面,会遍历 handlerAdapter
集合,来查找这个handlerAdapter
是否匹配这个Handler
,找到1个就直接返回,所以这个匹配是存在顺序的,第一步就是BeanNameUrlHandlerMapping
,也就是Bean的name是以“/”开头的都是会匹配到,并且这个类是实现了HttpRequestHandler
接口的类,才会返回这种适配器,返回这个适配器以后,继续往下执行代码,首先会先执行拦截器的前置方法preHandle()
方法,然后就会通过适配器调用到handle()
方法,这个从类中就可以看到强转一下直接调用,很简单。
有了上面的介绍,我们看这个就很好理解了,和上面的类似
对应的类内容:
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
// 强转判断
return (handler instanceof Controller);
}
@Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 强转调用方法
return ((Controller) handler).handleRequest(request, response);
}
// getLastModified() 方法已经标记废弃,这里省略
}
测试代码
@Component("/name")
public class BeanNameController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("实现Controller接口方式");
return null;
}
}
类图
可以发现这个类实现Ordered
接口,没有实现其他的特殊的接口,可以得出没有利用Spring的扩展点,接下来我们就看这个类的supports的方法,具体的代码如下:
@Override
public boolean supports(Object handler) {
return handler instanceof HandlerFunction;
}
只要handler的类型是HandlerFunction就返回true,所以在什么时候会返回true呢?由上一篇博客,我们可以得出,如果查找出来的Handler是RouterFunctionMapping,这个方法就会返回true
这个类主要用来适配添加了@Controller
和 @RequestMapping
注解的类,首先看一下这个类图
这儿实现了ApplicationContextAware
,BeanFactoryAware
,InitializingBean
接口,这3个接口实现的方法都会在容器启动时运行的,其中ApplicationContextAware
中的接口的方法不用看的,因为是父类实现了而且也没有调用这个类中的方法,接下来看下BeanFactoryAware
接口中的setBeanFactory
的方法,具体的代码如下:
@Override
public void setBeanFactory(BeanFactory beanFactory) {
if (beanFactory instanceof ConfigurableBeanFactory) {
this.beanFactory = (ConfigurableBeanFactory) beanFactory;
}
}
可以发现这个类就是Spring的bean的工厂设置给属性beanFactory,接下来我们继续看实现InitializingBean
中的afterPropertiesSet
方法,具体的代码如下:
public void afterPropertiesSet() {
// Do this first, it may add ResponseBody advice beans
//初始化 @ControllerAdvice 注解需要的东西,后面写篇博客介绍下这个注解
initControllerAdviceCache();
//获取调用的方法的参数
if (this.argumentResolvers == null) {
//Spring提供的一系列的HandlerMethodArgumentResolver,同时这儿也会调用用户自己添加的HandlerMethodArgumentResolver
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
//获取桥接方法的参数
if (this.initBinderArgumentResolvers == null) {
//Spring提供的一系列的HandlerMethodArgumentResolver,同时这儿也会调用用户自己添加的HandlerMethodArgumentResolver
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
//获取方法的返回的参数
if (this.returnValueHandlers == null) {
//Spring提供的一系列的HandlerMethodReturnValueHandler,同时这儿也会调用用户自己添加的HandlerMethodReturnValueHandler
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
上面就是完成了一系列的初始化的工作,同时添加了方法参数的解析器,以及一些处理方法返回值的处理器。这些在调用调用指定的方法和处理返回值的时候有用
看完了上面的那些扩展点,我们需要看下这个类中的supports的方法,具体的代码如下
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
RequestMappingHandlerAdapter
类的supportsInternal()
@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
return true;
}
判断类型是否是HandlerMethod,而第二个方法默认是返回true,所以这种的匹配的规则就是,加了@RequestMapping或者@Controller的类,同时这个类中的所有加@RequestMapping的方法都是会被解析成HandlerMethod类型的handler
上面介绍了适配方法Supports()
,接下来详细介绍一下RequestMappingHandlerAdapter
适配器的handle()
方法,这个方法跟其它适配类中的相同,都是调用对应的接口方法,但是这里需要考虑2点,
方法是如何被调用的?
方法的参数是如何传递过去的?
从Java语言的角度来考虑方法如何被调用,通常我们在一个类调用另外一个类的方法,可以想到有那么几种方式,
(1)最常见的就是创建这个类的对象,然后直接调用方法,但是必须知道方法名,但是这里调用,我们并不知道方法名
(2)通过Servlet来请求转发调用另外体一个Servlet的方法,但是这里我们被调用的方法只是一个普通的类
(3) 通过一个接口,对外开放一个方法,类似与上面的HttpServerServlet和Controller接口,但是这样的话,一个类只能写一个方法,不太合适,
(4) 通过反射
反射来调用方法,该种适配器就是采用的反射来调用接口方法
接下来我们来看对应的方法,首先调用到AbstractHandlerMethodAdapter
类的handle()
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
调用handleInternal(request, response, (HandlerMethod) handler)
方法
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
// 检查请求的会话是否支持
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
// 默认的情况下这个值是false,所以会执行else中的代码
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No synchronization on session demanded at all...
// 调用对应的方法
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
继续调用URL映射的方法 invokeHandlerMethod(request, response, handlerMethod)
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
// 将request和response包装成ServletWebRequest对象
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
//返回Web数据绑定器工厂
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
//返回模型工厂
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 返回要调用的方法
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
// 设置参数的解析器
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
// 设置方法的返回类型参数
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
// 设置方法的返回类型参数
invocableMethod.setDataBinderFactory(binderFactory);
// 设置对应的方法参数名发现器,就是用来查找方法的参数名称的,因为JDK自带的只会返回arg0
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
// 创建模型和试图的容器
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
// 设置只读的Input属性
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
// 初始化模型
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
// 设置忽略重定向默认模型
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
// 创建异步的请求
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);
}
// 真正的调用处理
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
// 获取对应的modelAndView对象
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
上面就进行一系列的参数的赋值,最后真正调用方法的 invokeAndHandle(webRequest, mavContainer)
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()) {
disableContentCachingIfNecessary(webRequest);
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;
}
}
继续调用 invokeForRequest(webRequest, mavContainer, providedArgs)
@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));
}
// 通过反射调用该方法
return doInvoke(args);
}
上面的代码包括两步,第1 通过 getMethodArgumentValues()
方法来获取参数,第2 通过doInvoke()
来执行对应的方法,是通过反射来执行,然后将结果返回,
那么首先来看获取实参的处理,整体的一个思路就是 首先获取到该方法的全部形参,然后通过参数解析器,现在请求里面通过类型找,然后在通过形参名找,如此把请求携带的参数自动赋值到实参里面
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 获取方法的参数, 注意看这里是直接调用方法,前面没有类名,
// 本方法处于InvocableHandlerMethod类,是HandlerMethod的子类
// 我们前面获取的映射器是InvocableHandlerMethod的子类,所以直接调用
// 所以获取到的就是该方法的形参列表数组
// 点进到 getMethodParameters()方法里面 就是HandlerMethod类的getter方法
// 下面看到了其关系图
MethodParameter[] parameters = getMethodParameters();
// 如果参数为空,代表该方法不需要形参,直接返回结合
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
// 创建参对象 存储方法实参
Object[] args = new Object[parameters.length];
// 通过遍历,根据方法形参,借助参数解析器,去请求里面查询实参
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
// 初始化参数名称的发现器
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
// 这儿一般的情况下会返回Null
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
// 查找对应的参数的处理器,主要是遍历所有的参数的处理器,调用处理中的support方法,只要满足就返回true
// 不同的参数类型有不同的处理器
// 参数类型的初始化在后面介绍
if (!this.resolvers.supportsParameter(parameter)) {
// 如果没有找到参数解析器 就报异常
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
// 找到了就直接执行参数处理器的resolveArgument() 解析参数 获取实参
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;
}
上面看到我们首先会查询该形参是否存在匹配的参数解析器,如果不存在就报异常,看一下supportsParameter(parameter)
方法
@Override
public boolean supportsParameter(MethodParameter parameter) {
return getArgumentResolver(parameter) != null;
}
继续调用 getArgumentResolver()
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
// 先尝试从缓存中获取 这个是存在缓存的
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {
// 遍历所有的参数处理器,调用每一个参数处理器的 supportsParameter 方法,直到返回的结果为true为止
// 下面介绍该参数解析器在哪里初始化的
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
// 依次判断
if (resolver.supportsParameter(parameter)) {
result = resolver;
// 如果匹配到 就存入缓存
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}
上面我们介绍了如何匹配参数解析器,接下来我们看一下resolveArgument()
当我们,其实注意看一下类结构,这前后用的几个方法 supportsParameter()
、resolveArgument()
、getArgumentResolver()
都是HandlerMethodArgumentResolverComposite
类的,而且并排的三个方法
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 继续调用 getArgumentResolver() 方法获取到对应的参数解析器
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);
}
我们方法的形参有不同的类型,有包装类,字符串,基本数据类型,数组、引用数据类型等等,那么会分成2种情况,第一种就是普通的类型,第二种就是引用对象,SpringMVC会自动把参数封装到对象里面
普通的参数,一般会跳转到 AbstractNamedValueMethodArgumentResolver
类,然后我们查看其中的resolveArgument()
,具体的代码如下:
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 获取方法的形参名信息 NamedValueInfo是一个静态内部类 包含这个形参的名称、required、defaultValue信息
// 如果形参有@RequestParam注解 则注解信息都会保存在NamedValueInfo这里
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
// 获取包装的方法参数的类型
MethodParameter nestedParameter = parameter.nestedIfOptional();
// 从上面的namedValueInfo单独取出形参名
Object resolvedName = resolveEmbeddedValuesAndExpressions(namedValueInfo.name);
// 非空判断
if (resolvedName == null) {
throw new IllegalArgumentException(
"Specified name must not resolve to null: [" + namedValueInfo.name + "]");
}
// 获取参数的值
Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
// 获取的值为空
if (arg == null) {
// 查看是否有默认值,设置默认值
if (namedValueInfo.defaultValue != null) {
arg = resolveStringValue(namedValueInfo.defaultValue);
}
else if (namedValueInfo.required && !nestedParameter.isOptional()) {
// 判断是否是必须要值,如果是直接抛出异常
handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
}
// 处理空值,如果这个值的类型是Boolean类型,就设置为false,其他的情况下都是原来的默认值
arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
}
else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
// 获取值为""同时默认值也不为空,那么就将默认值设置进去
arg = resolveStringValue(namedValueInfo.defaultValue);
}
// 进行类型转换 因为URL请求接收的一般都是String类型,而我们的形参存在各种类型
if (binderFactory != null) {
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());
}
// 最极端情况 值为空&&默认值为空&&必填 报异常
if (arg == null && namedValueInfo.defaultValue == null &&
namedValueInfo.required && !nestedParameter.isOptional()) {
handleMissingValueAfterConversion(namedValueInfo.name, nestedParameter, webRequest);
}
}
handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
//返回处理好的参数的
return arg;
}
由于RequestParamMethodArgumentResolver
类没有实现HandlerMethodArgumentResolver
接口,所以这儿调用的是父类的resolveArgument
方法,但是获取对应参数的值是调用子类RequestParamMethodArgumentResolver
的resolveName
方法,具体的代码如下:
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<MultipartFile> files = multipartRequest.getFiles(name);
if (!files.isEmpty()) {
arg = (files.size() == 1 ? files.get(0) : files);
}
}
if (arg == null) {
// 重点在这里 还是调用请求的API来获取参数 返回是个数组
String[] paramValues = request.getParameterValues(name);
if (paramValues != null) {
arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
}
}
return arg;
}
我们终于看到对应参数的获取方法resolveName()
,就是利用了servlet的api来获取指定的参数的值的。我们继续看上面的resolveArgument()
剩余的代码,注释内容都在上面写清楚了,这里就不在重复了。
当我们的形参有引用类型,SpringMVC会把参数自动封装到对象,其中SpringMVC会首先创建这个对象,然后拿其中的字段去匹配,这个时候我们使用的适配器为
ModelAttributeMethodProcessor
的resolveArgument()
@Override
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");
// 获取到引用类型形参名
String name = ModelFactory.getNameForParameter(parameter);
ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
if (ann != null) {
mavContainer.setBinding(name, ann.binding());
}
Object attribute = null;
BindingResult bindingResult = null;
if (mavContainer.containsAttribute(name)) {
attribute = mavContainer.getModel().get(name);
}
else {
// Create attribute instance
try {
// 创建对象示例 即使没有匹配到任何参数,但是对象肯定有 只是字段值都为空
attribute = createAttribute(name, parameter, binderFactory, webRequest);
}
catch (BindException ex) {
if (isBindExceptionRequired(parameter)) {
// No BindingResult parameter -> fail with BindException
throw ex;
}
// Otherwise, expose null/empty value and associated BindingResult
if (parameter.getParameterType() == Optional.class) {
attribute = Optional.empty();
}
else {
attribute = ex.getTarget();
}
bindingResult = ex.getBindingResult();
}
}
if (bindingResult == null) {
// Bean property binding and validation;
// skipped in case of binding failure on construction.
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
if (binder.getTarget() != null) {
if (!mavContainer.isBindingDisabled(name)) {
// 绑定请求参数 也就是在这里将请求和对象绑定起来
bindRequestParameters(binder, webRequest);
}
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new BindException(binder.getBindingResult());
}
}
// Value type adaptation, also covering java.util.Optional
if (!parameter.getParameterType().isInstance(attribute)) {
attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
}
bindingResult = binder.getBindingResult();
}
// Add resolved attribute and BindingResult at the end of the model
Map<String, Object> bindingResultModel = bindingResult.getModel();
mavContainer.removeAttributes(bindingResultModel);
mavContainer.addAllAttributes(bindingResultModel);
return attribute;
}
上面我们看到了匹配的大概方法,这个依次递进贴绑定方法,会比较麻烦,可以通过IDEA打断点来查看,只要能找到其中遍历的那个代码就可以了
@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));
}
// 通过反射调用该方法
return doInvoke(args);
}
上面我们介绍了获取参数的方法 getMethodArgumentValues()
,接下来我们看一下调用方法doInvoke()
@Nullable
protected Object doInvoke(Object... args) throws Exception {
// 获取方法
Method method = getBridgedMethod();
ReflectionUtils.makeAccessible(method);
try {
if (KotlinDetector.isSuspendingFunction(method)) {
return CoroutinesUtils.invokeSuspendingFunction(method, getBean(), args);
}
// 重点在这里 通过反射来调用方法
return method.invoke(getBean(), args);
}
catch (IllegalArgumentException ex) {
assertTargetBean(method, 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);
}
}
}
我们已经说完了invokeForRequest()
方法,获取到参数,成功调用方法,加下来继续回到invokeAndHandle()
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// 设置响应的状态码,这儿应该没有调用完,会是null
setResponseStatus(webRequest);
// 如果返回值为空
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
// 禁用缓存
disableContentCachingIfNecessary(webRequest);
// 将这个变量设置为true
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {// 这个有值
// 将这个变量设置为true
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;
}
}
上面的代码会调用handleReturnValue()
,具体的代码如下:
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 查找指定的方法返回值的处理器
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
// 调用指定的方法返回值的处理器中这个方法
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
上面的代码主要是通过selectHandler(returnValue, returnType);
来查找对应的方法返回值的处理器,具体的代码如下:
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
// 判断是否是异步
boolean isAsyncValue = isAsyncReturnValue(value, returnType);
// 遍历所有的方法返回值的处理器的supportsReturnType方法,只要找到匹配的就直接返回
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
continue;
}
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
这儿原理和上面的找方法参数的处理器的机制差不多,都是遍历所有的处理器,调用对应处理器的supportsReturnType方法,一旦满足条件的话,就直接返回。
接下来我们继续回到invokeHandlerMethod()
,我们在这个方法中已经调用了invokeAndHandle()
,在该方法的的最后,又调用了getModelAndView()
@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
modelFactory.updateModel(webRequest, mavContainer);
// 判断这个值是否是true,一般返回的如果不是视图的话,这个值就是true
// 因为我们现在的项目基本都是前后端分离,所以这里就直接返回了
if (mavContainer.isRequestHandled()) {
return null;
}
ModelMap model = mavContainer.getModel();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) {
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
}
return mav;
}
至此整个方法的调用的过程就讲完了,简单总结一下:
supportsParameter()
,如果满足了就直接返回这个参数处理器,然后调用这个参数处理器的resolveArgument()
,上面我们用到了参数解析器,我们通过遍历,找到对应的参数解析器,那么这些参数解析器是如何初始化的呢?
我们之前提到 RequestMappingHandlerAdapter
这个类实现了 InitializingBean
接口,当SpringMVC容器启动的适合,就会调用afterPropertiesSet()
public void afterPropertiesSet() {
// Do this first, it may add ResponseBody advice beans
//初始化 @ControllerAdvice 注解需要的东西,后面写篇博客介绍下这个注解
initControllerAdviceCache();
//获取调用的方法的参数
if (this.argumentResolvers == null) {
//Spring提供的一系列的HandlerMethodArgumentResolver,同时这儿也会调用用户自己添加的HandlerMethodArgumentResolver
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
//获取桥接方法的参数
if (this.initBinderArgumentResolvers == null) {
//Spring提供的一系列的HandlerMethodArgumentResolver,同时这儿也会调用用户自己添加的HandlerMethodArgumentResolver
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
//获取方法的返回的参数
if (this.returnValueHandlers == null) {
//Spring提供的一系列的HandlerMethodReturnValueHandler,同时这儿也会调用用户自己添加的HandlerMethodReturnValueHandler
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
我们上面用到的参数解析器 和 返回值处理器 就是在这里被初始化的。
private List<HandlerMethodArgumentResolver> getDefaultInitBinderArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(20);
// Annotation-based argument resolution
// 注意看这里添加第一次RequestParamMethodArgumentResolver
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
resolvers.add(new RequestParamMapMethodArgumentResolver());
resolvers.add(new PathVariableMethodArgumentResolver());
resolvers.add(new PathVariableMapMethodArgumentResolver());
resolvers.add(new MatrixVariableMethodArgumentResolver());
resolvers.add(new MatrixVariableMapMethodArgumentResolver());
resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new SessionAttributeMethodArgumentResolver());
resolvers.add(new RequestAttributeMethodArgumentResolver());
// Type-based argument resolution
resolvers.add(new ServletRequestMethodArgumentResolver());
resolvers.add(new ServletResponseMethodArgumentResolver());
// Custom arguments
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}
// Catch-all
resolvers.add(new PrincipalMethodArgumentResolver());
// // 注意看这里添加第二次RequestParamMethodArgumentResolver
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
return resolvers;
}
注意上面是加了两个RequestParamMethodArgumentResolver
对象,只不过第一次的参数的值是false
,第二次参数的值为true
。那么这个两个值的区别是啥?我们还需要看 RequestParamMethodArgumentResolver
类的supportsParameter
方法,具体的代码如下:
public boolean supportsParameter(MethodParameter parameter) {
// 是否存在 RequestParam 注解
if (parameter.hasParameterAnnotation(RequestParam.class)) {
if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
return (requestParam != null && StringUtils.hasText(requestParam.name()));
}
else {
return true;
}
}
else {
// 是否存在RequestPart注解 该注解用于复杂的请求域
if (parameter.hasParameterAnnotation(RequestPart.class)) {
return false;
}
parameter = parameter.nestedIfOptional();
// 是否是请求的参数
if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {
return true;
}
// 区别在这,如果是true的话,基本的类型是会返回true 否则直接返回false
else if (this.useDefaultResolution) {
return BeanUtils.isSimpleProperty(parameter.getNestedParameterType());
}
else {
return false;
}
}
}
上面两个RequestParamMethodArgumentResolver
的区别就在于如果是true的话,基本的类型是会返回true,如果不是true,就直接跳过,代表我们匹配参数的时候,基本数据类型放到最后在匹配,优先匹配引用类型去匹配,也就是包装类的优先级高于基本数据类型
HandlerAdapter 采用了适配器模式,主要是为了调用不同的接口方法,那么这里面最麻烦的就是RequestMappingHandlerAdapter
,因为这个方法支持注解,而且要自动把参数赋值,这里面比较复杂的处理处理参数,需要长时间的看方法的调用