spring源码------一个请求在spring中的处理过程(HandlerAdapter的选择跟请求的处理)代码及流程图说明 (4)

本文主要是介绍spring源码------一个请求在spring中的处理过程(HandlerAdapter的选择跟请求的处理)代码及流程图说明 (4),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

      • 前提
      • 1.`HandlerAdapter`对象
      • 2. 获取`HandlerAdapter`对象的`getHandlerAdapter`方法
        • 2.1 `getHandlerAdapter`方法分析
          • 2.1.1 容器中的`HandlerAdapter`对象以及实现的选择
          • 2.1.2 `RequestMappingHandlerAdapter`对`supports`的实现
      • 3. `HandlerAdapter`对请求处理的`hadler`方法
        • 3.1 逻辑交给子类实现的`hadler`方法
        • 3.2 `handleInternal`逻辑
          • 3.2.1 调用处理方法的逻辑`invokeHandlerMethod`
          • 3.2.2 调用我们定义的controller类中的逻辑的步骤`invokeAndHandle`

前提

 这部分的内容是基于spring源码------一个请求在spring中的处理过程(从FrameworkServlet规范到DispatcherServlet)代码及流程图说明 (2)文章的进一步的对doDispatch方法的内部分析的。前面已经讲了getHandler方法的分析,这里就接着讲下一步的getHandlerAdapter方法

	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {......//获取封装了HandlerInterceptor的HandlerExecutionChainmappedHandler = getHandler(processedRequest);//如果不存在对应的处理链则返回if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}//从HandlerExecutionChain中获取Handler然后寻找合适的HandlerAdapterHandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());......
}

1.HandlerAdapter对象

 先根据官方的类注释来了解一下HandlerAdapter类的作用。这个类的作用是spring中的一个可扩展的接口类,允许对spring的允许对核心MVC工作流进行参数化,在DispatcherServlet中会对所有的这个类的实现类进行遍历,对于想要指定自己的web的工作流的开发者来说,可以实现这个接口。需要注意的是,实现这个接口的类,一般还需要实现Ordered接口或者贴上@Order注解来指定优先级。接下来对这个类的方法进行分析:

public interface HandlerAdapter {boolean supports(Object handler);@NullableModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;long getLastModified(HttpServletRequest request, Object handler);
}

 这几个方法的作用说明:

  1. supports
     这个方法作用就是在寻找合适的HandlerAdapter的时候,根据这个方法的返回结果来表示当前的HandlerAdapter是否适合处理当前的请求
  2. handle
     这个方法就是处理请求的方法,返回一个ModelAndView视图对象
  3. getLastModified
     这个是为了支持一些HandlerAdapter类不支持HttpServletgetLastModified方法用的。这个方法前面介绍过你们可以看看前面的文章。

2. 获取HandlerAdapter对象的getHandlerAdapter方法

2.1 getHandlerAdapter方法分析

 直接先看getHandlerAdapter方法的逻辑:

	protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {//寻找合适的HandlerAdapterif (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对象的handlerAdapters这个成员遍历,寻找合适的HandlerAdapter然后返回。这里需要注意的是,找到了第一个合适的HandlerAdapter方法就会结束,所以HandlerAdapter在handlerAdapters中的顺序很重要,这也是上面的HandlerAdapter类描述中建议实现Ordered接口或者贴上@Order注解。
 关于handlerAdapters这个变量,他的填充是在上下文刷新的后进行填充的跟,逻辑在DispatchServlet类的initHandlerAdapters方法中。

	private void initHandlerAdapters(ApplicationContext context) {this.handlerAdapters = null;//是否寻找所有的HandlerAdapter,默认为trueif (this.detectAllHandlerAdapters) {//在ApplicationContext中查找所有handleradapter,包括祖先上下文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,类型为HandlerAdapter的beanHandlerAdapter 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.}}如果容器中没有,则用默认的,一般情况下不会到这一波,关于默认的配置会读取DispatcherServlet.properties配置文件中的配置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");}}}
2.1.1 容器中的HandlerAdapter对象以及实现的选择

 在spring中HandlerAdapter的子实现类会注册到容器中的有以下几个,RequestMappingHandlerAdapter,HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter。这些类都是在WebMvcConfigurationSupport中用@Bean注解注册到容器中的。

public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {@Beanpublic RequestMappingHandlerAdapter requestMappingHandlerAdapter(ContentNegotiationManager mvcContentNegotiationManager,FormattingConversionService mvcConversionService, Validator mvcValidator) {RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();}@Beanpublic HttpRequestHandlerAdapter httpRequestHandlerAdapter() {return new HttpRequestHandlerAdapter();}@Beanpublic SimpleControllerHandlerAdapter simpleControllerHandlerAdapter() {return new SimpleControllerHandlerAdapter();}}

 其中RequestMappingHandlerAdapter的父类AbstractHandlerMethodAdapter实现了Ordred接口,设置的优先级,高于HttpRequestHandlerAdapterSimpleControllerHandlerAdapter这两个类(没事试下Ordred接口,也没有贴@Order注解),所以遍历的顺序也是这个类最先。因此每次请求第一个调用的就是RequestMappingHandlerAdapter方法。后面的分析都用这个类进行分析。

private int order = Ordered.LOWEST_PRECEDENCE;public void setOrder(int order) {this.order = order;}
2.1.2 RequestMappingHandlerAdaptersupports的实现

RequestMappingHandlerAdapter的父类抽象类AbstractHandlerMethodAdapter实现了HandlerAdapter接口supports方法。

	@Overridepublic final booleansupports(Object handler) {//handler是HandlerMethod的类型的,并且满足子类实现的判断逻辑return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));}

 看看由RequestMappingHandlerAdapter实现的supportsInternal方法

	@Overrideprotected boolean supportsInternal(HandlerMethod handlerMethod) {return true;}

 发现返回的是一个固定的true,因为任何方法参数和返回值类型都将以被处理。

3. HandlerAdapter对请求处理的hadler方法

3.1 逻辑交给子类实现的hadler方法

 在选择完HandlerAdapter后,会先调用HandlerExecutionChain的拦截器的preHandle方法。然后就会调用HandlerAdapter对请求进行处理了。
 进入到AbstractHandlerMethodAdapterhandle方法,逻辑完全委托给了子类去实现handleInternal方法来处理。

	public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {return handleInternal(request, response, (HandlerMethod) handler);}

 实现的逻辑在RequestMappingHandlerAdapterhandleInternal方法中。

3.2 handleInternal逻辑
	protected ModelAndView handleInternal(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ModelAndView mav;//检查请求方法是不是支持的,以及如果设置了需要session但是却不存在session(意思是当前请求的时候已经存在session了)checkRequest(request);// Execute invokeHandlerMethod in synchronized block if required.//是否在session级别上执行同步操作,默认为falseif (this.synchronizeOnSession) {//获取session,如果不存在session,不创建新的sessionHttpSession session = request.getSession(false);//如果session不为空则在session上加锁if (session != null) {Object mutex = WebUtils.getSessionMutex(session);synchronized (mutex) {//调用处理方法mav = invokeHandlerMethod(request, response, handlerMethod);}}else {//不存在session则不加锁处理// No HttpSession available -> no mutex necessarymav = invokeHandlerMethod(request, response, handlerMethod);}}else {//不需要session同步,则直接调用处理方法mav = invokeHandlerMethod(request, response, handlerMethod);}//如果返回头不包含缓存控制相关的信息(Cache-Control属性表示缓存控制(缓存指的缓存服务器)相关的,这个可以自行查询相关资料了解)if (!response.containsHeader(HEADER_CACHE_CONTROL)) {//根据,handlerMethod构建一个SessionAttributesHandler对象(会把对应的SessionAttributes注解封装起来),并判断是否存在@SessionAttributes注解属性if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {//生产Http头applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);}else {//准备返回prepareResponse(response);}}return mav;}

 上面的方法逻辑也算是比较简单的,可以分为三步。

  1. 调用处理方法之前的请求检查,检查是否是支持的请求方式,请求是否在同一个session中
  2. 调用请求的处理方法,返回视图对象ModelAndView
  3. 检查response对象是否有缓存控制相关的操作,没有则进行相关的处理
3.2.1 调用处理方法的逻辑invokeHandlerMethod

 上面的核心的步骤就在invokeHandlerMethod方法中,这个方法中的逻辑分支比较多,基本上一行代码就是一个逻辑步骤。相当于是一个聚合,spring中的代码很多都是这样的。这里就每一个步骤做什么进行讲解,但是不会深入到每个步骤的内部逻辑:

	protected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {//创建ServletWebRequestServletWebRequest webRequest = new ServletWebRequest(request, response);try {//获取处理类中的所有的贴有@InitBinder注解的方法,然后封装处理为一个WebDataBinderFactory对象WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);//获取处理类中的所有的贴有@ModelAttribute注解的方法,然后封装处理为一个WebDataBinderFactory对象ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);//根据handlerMethod创建ServletInvocableHandlerMethod对象ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);//设置请求参数解析器,这个参数解析器可以自定义if (this.argumentResolvers != null) {invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}//设置返回参数处理器,这个也可以自定义if (this.returnValueHandlers != null) {invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);}//设置前面处理的封装@InitBinder注解信息的WebDataBinderFactoryinvocableMethod.setDataBinderFactory(binderFactory);//设置方法跟构造器参数解析的类invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);//创建视图容器ModelAndViewContainer mavContainer = new ModelAndViewContainer();//把上下文中的跟这个请求相关的请求放到返回时的视图中, 这里的相关的请求指的是请求包含或者请求转发mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));//调用所有 贴有ModelAttribute注解的方法,将返回结果添加到视图中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;}return getModelAndView(mavContainer, modelFactory, webRequest);}finally {webRequest.requestCompleted();}}

 方法的逻辑比较复杂,先看看简单的看看步骤:

  1. 获取请求处理类中贴有@InitBinder注解的方法,然后创建一个WebDataBinderFactory
  2. 获取请求处理类中贴有@ModelAttribute注解的方法,然后创建一个ModelFactory
  3. 将请求处理类封装为一个ServletInvocableHandlerMethod对象
  4. 设置请求参数解析器组合类
  5. 设置返回参数解析器组合类
  6. 设置前面封装@InitBinder注解的WebDataBinderFactory
  7. 设置方法跟构造器参数解析的类
  8. 创建视图容器ModelAndViewContainer,然后初始化并设置初始的一些模型
  9. 处理异步请求相关的
  10. 调用请求处理类,对应的处理方法。

  这里的步骤虽然多,但是还是可以简单的分为用户处理方法调用前的准备工作,跟调用处理方法两个步骤。这里对于准备的工作不做解析,主要看处理方法invokeAndHandle的调用。其余方法的逻辑后面会有文章进行介绍,这里就讲跟主题相关的。

3.2.2 调用我们定义的controller类中的逻辑的步骤invokeAndHandle

 进入到invokeAndHandle方法

	public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {//调用处理的方法,获取处理的结果Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);//如果方法上有ResponseStatus注解,则按照我们设置的返回状态进行返回setResponseStatus(webRequest);//如果方法结果为nullif (returnValue == null) {//如果请求没有修改过,或者返回状态不为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方法。

	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);}@Nullableprotected Object doInvoke(Object... args) throws Exception {ReflectionUtils.makeAccessible(getBridgedMethod());try {return getBridgedMethod().invoke(getBean(), args);}......

 逻辑很简单就是调用自己扩展的参数处理方法,用处理过后的参数作为入参然后用反射调用定义的逻辑处理方法(就是我们controller中定义的方法),最后返回调用的结果。

 到这里整个HandlerAdapter的选择跟请求的处理逻辑就结束了。

这篇关于spring源码------一个请求在spring中的处理过程(HandlerAdapter的选择跟请求的处理)代码及流程图说明 (4)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/783322

相关文章

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

Linux join命令的使用及说明

《Linuxjoin命令的使用及说明》`join`命令用于在Linux中按字段将两个文件进行连接,类似于SQL的JOIN,它需要两个文件按用于匹配的字段排序,并且第一个文件的换行符必须是LF,`jo... 目录一. 基本语法二. 数据准备三. 指定文件的连接key四.-a输出指定文件的所有行五.-o指定输出

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代

一篇文章彻底搞懂macOS如何决定java环境

《一篇文章彻底搞懂macOS如何决定java环境》MacOS作为一个功能强大的操作系统,为开发者提供了丰富的开发工具和框架,下面:本文主要介绍macOS如何决定java环境的相关资料,文中通过代码... 目录方法一:使用 which命令方法二:使用 Java_home工具(Apple 官方推荐)那问题来了,

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

详解SpringBoot+Ehcache使用示例

《详解SpringBoot+Ehcache使用示例》本文介绍了SpringBoot中配置Ehcache、自定义get/set方式,并实际使用缓存的过程,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录摘要概念内存与磁盘持久化存储:配置灵活性:编码示例引入依赖:配置ehcache.XML文件:配置

Java 虚拟线程的创建与使用深度解析

《Java虚拟线程的创建与使用深度解析》虚拟线程是Java19中以预览特性形式引入,Java21起正式发布的轻量级线程,本文给大家介绍Java虚拟线程的创建与使用,感兴趣的朋友一起看看吧... 目录一、虚拟线程简介1.1 什么是虚拟线程?1.2 为什么需要虚拟线程?二、虚拟线程与平台线程对比代码对比示例:三

Java中的.close()举例详解

《Java中的.close()举例详解》.close()方法只适用于通过window.open()打开的弹出窗口,对于浏览器的主窗口,如果没有得到用户允许是不能关闭的,:本文主要介绍Java中的.... 目录当你遇到以下三种情况时,一定要记得使用 .close():用法作用举例如何判断代码中的 input