您的位置:首页 > 编程语言 > Java开发

springMVC方法参数值注入简要源码分析

2017-04-15 18:20 197 查看

1. 开始之前的唠嗑

懒惰是前进道路上最大的敌人,懒着懒着心就飘远了,荒废了许久的博客今天稍微重新拾起来,今天带了一篇springMVC源码分析之参数注入。

2. 步入正题

稍微了解springMVC的同学知道,在springMVC的应用中,它采用的中心转发的一种设计模式吧,中心转发这个词是我自己鼓捣出来了,请各位自行理解,何谓中心转发呢,其实就是所有请求一律经过spring中心拦截器拦截派发,这里引出了第一个疑点,springMVC的中心拦截器在哪儿呢,其实就是DispatcherServet这个类了,所有前端发出的请求都经由此类的方法doDispatch方法处理,下面上一张今天我们要将的参数值注入的入口方法

// 今天的入口方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());


上面的这段代码是doDispatch方法的一段,springMVC在初始化好web应用上下文后,请求最终都会路由这里进行真正的方法调用。该方法是HandlerAdapter接口的方法,简单看下这个接口的实现类有哪些,如下图所示


如上图所示的实现,我们进入第一个实现类中去看看他的源码,究竟怎样一步一步实现参数值注入的。在AbstractHandlerMethodAdapter类中,它的代码逻辑如下


可以在这个类中可以看到,其实它什么也没有做,只是将方法委托给该类的handleInternal方法,该方法是一个抽象方法,正如该类的名字一样,该类只是提供一个基础模版,具体的实现交给其子类实现,用设计模式的专业术语来说就是模版方法。

handleInternal这个方法的实现在哪儿呢,当然是它的子类了,通过intellij IDEA的快捷键,我们可以快速定位到其子类,就是RequestMappingHandlerAdapter这个类,RequestMappingHandlerAdapter的handleInternal方法如下截图


这个类中的这个方法核心代码在最后一句代码中,即是如下代码

return invokeHandlerMethod(request, response, handlerMethod);


先对上面代码进行简要说明,首先在这个方法中有个checkAndPrepare方法,此方法主要是检察一下request请求方法是否被支持,是否需要session已经是否缓存请求头等,各位可自行去源码中查看,这个方法的末尾最终是去调用如上的的方法,那我们就进到这个方法去瞧瞧


从这个方法开始,spring开始为真正的处理请求做一些准备工作了。

1. 封装request,response

2. 创建请求参数自定义工厂类,我们经常在代码中看到的@initBinder注解,就是最终交由这个工厂类创建的实例对象来处理

3. 创建方法调用对象ServletInvocableHandlerMethod的实例,最终会交给此创建的对象进行真正的处理,你可以在如上截图的代码中看到,如下代码即是

requestMappingMethod.invokeAndHandle(webRequest, mavContainer);


我们进入这个方法详细看看


在这个方法中的第一句代码可以看到一个方法invokeForRequest,然后在这个方法的后台都是针对这个方法的返回值进行处理,所以并不是我们今天要讨论的主题,所以我们直接进入invokeForRequest方法去看。照惯例,先上方法截图


这个方法非常的简单,首先调用getMethodArgumentValues方法获取参数和参数的值,具体的获取过程,我们在下文会继续讲到,也是今天的主要内容,然后通过反射(doInvoke)进行对应方法的调用,获取执行结果。

下面看与今天要讲的参数值注入密切相关的方法getMethodArgumentValues,这个方法会获取每个请求参数与对应的值。该方法截图如下


这个方法也还是比较简单的,循环遍历请求参数,然后委托给HandlerMethodArgumentResolver接口的方法resolveArgument
去进行参数值解析。

下面稍微仔细说明一下

1.  如果有提供的参数值,直接返回提供的参数值

2. 检察参数类型是否支持

3. 查询参数解析器,找到后委托给参数解析器解析

4. 如果未找到抛出异常

通过上面的步骤,我们知道,要给参数注入参数的值,最终是通过HandlerMethodArgumentResolver接口的实现类HandlerMethodArgumentResolverComposite实现的。HandlerMethodArgumentResolverComposite类实现是采用的组合模式的设计策略,我们可以在类RequestMappingHandlerAdapter中的方法afterPropertiesSet方法中看到HandlerMethodArgumentResolverComposite中的resovler的初始化代码,如下所示

        @Override
public void afterPropertiesSet() {
// Do this first, it may add ResponseBody advice beans
initControllerAdviceCache();
 
if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.initBinderArgumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}


了解spring的都知道afterPropertiesSet方法是在什么时候执行的,当RequestMappingHandlerAdapter类创建好后就立即执行了该方法,通过这个方法我们可以看到,通过这个方法增加默认的参数解析器和我们在代码中配置的参数解析器,就是上文所说的@initBinder注解注入的参数解析类

我们看下默认提供的参数解析器有哪些


通过上面的截图我们可以看到spring其实已经内置了大部分的参数解析,基本能够解决我们日常开发工作中的80%了。

下面我们结合AbstractNamedValueMethodArgumentResolver实现类的 resolveArgument 方法进行说明


该方法首先创建一个键值对对象,然后首先查看是否有自定义参数解析器解析,如果有用自定义参数解析器解析,最后在进行后续的参数解析。

今天的源码分析就到这里

3. 总结

今天的解析主要分析了请求处理的整个流程以及在什么时候进行参数解析,同时了解了用到的部分设计模式,对spring请求处理的前半部分有了一个更加清晰的认知

4. 问题

在看源码的过程中一直没有找到HandlerMethod实例的创建过程,留待后续的阅读中去解读
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐