Spring-MVC 源码分析--DispatcherServlet 初始化

文章目录

  • 前言
  • [一、DispatcherServlet 概念以及作用:](#一、DispatcherServlet 概念以及作用:)
  • [二、请求分发器 HandleMapping :](#二、请求分发器 HandleMapping :)
    • [2.1 HandleMapping 的概念及其作用:](#2.1 HandleMapping 的概念及其作用:)
    • [2.2 HandleMapping 的三种默认实现](#2.2 HandleMapping 的三种默认实现)
      • [2.2.1 HandleMapping 实现的时机:](#2.2.1 HandleMapping 实现的时机:)
      • [2.2.2 RequestMappingHandlerMapping作用 :](#2.2.2 RequestMappingHandlerMapping作用 :)
      • [2.2.3 BeanNameUrlHandlerMapping 作用:](#2.2.3 BeanNameUrlHandlerMapping 作用:)
      • [2.2.4 RouterFunctionMapping 作用:](#2.2.4 RouterFunctionMapping 作用:)
  • [三、HandlerAdapter 介绍接作用 :](#三、HandlerAdapter 介绍接作用 :)
    • [3.1 HandlerAdapter 的作用:](#3.1 HandlerAdapter 的作用:)
    • [3.1 HandlerAdapter的几种默认实现:](#3.1 HandlerAdapter的几种默认实现:)
      • [3.1.1 .HttpRequestHandlerAdapter](#3.1.1 .HttpRequestHandlerAdapter)
      • [3.1.2 .SimpleControllerHandlerAdapter](#3.1.2 .SimpleControllerHandlerAdapter)
      • [3.1.3 .RequestMappingHandlerAdapter](#3.1.3 .RequestMappingHandlerAdapter)
      • [3.1.4 .HandlerFunctionAdapter](#3.1.4 .HandlerFunctionAdapter)
  • 总结

前言

我们知道一个http 请求到达服务端后是通过DispatcherServlet 进行请求的分发,而分发的逻辑具体就是要找到这个请求合适的HandleMapping 来处理,那么HandleMapping 是什么,它是怎么根据请求路径判断 哪个HandleMapping 来处理请求呢,HandleMapping 分发请求后,怎么完成对方法参数的解析,又是如何将方法的返回值给封装成浏览器需要的对象?


一、DispatcherServlet 概念以及作用:

DispatcherServlet 是 Spring MVC 框架的核心组件之一,它是一个前端控制器(Front Controller)。

DispatcherServlet 充当了统一的请求处理器和请求调度中心的角色。当一个 HTTP 请求发起到应用程序时,它首先被 DispatcherServlet 接收并处理。DispatcherServlet 根据请求的特征和配置的规则,将请求分配给合适的处理器来进行处理,并将处理结果返回给客户端。

DispatcherServlet 的主要功能包括:

  1. 请求的接收和解析:DispatcherServlet 接收并解析 HTTP 请求,包括请求的 URL、参数、头信息等。

  2. HandlerMapping:DispatcherServlet 使用一个或多个 HandlerMapping 来决定请求应该由哪个处理器来处理。HandlerMapping 根据请求的 URL 和其他条件,将请求映射到相应的处理器(即控制器)。

  3. HandlerAdapter:DispatcherServlet 使用一个或多个 HandlerAdapter 来适配不同类型的处理器。HandlerAdapter 将处理器适配成统一的处理器方法的调用方式,以便进行统一的处理和返回结果。

  4. 视图解析和渲染:DispatcherServlet 使用视图解析器(ViewResolver)来解析处理器返回的逻辑视图名,并将其渲染为最终的响应结果。可以是HTML、JSON、XML等不同格式的响应。

  5. 异常处理和错误页面:DispatcherServlet 提供了处理请求过程中发生的异常和错误的机制。它可以捕获并处理异常,并将错误信息渲染为指定的错误页面或其他响应。

通过配置和定制 DispatcherServlet,可以灵活地控制请求的处理流程,进行拦截、验证、权限控制等操作,从而实现灵活、可扩展的 Web 应用程序,通过对DispatcherServlet 初始化实际上就是对HandlerMapping ,HandlerAdapter 的初始化。

可见一个DispatcherServlet 的bean 完成了http 请求的全部流程,想要知晓它的工作情况,就需要对它内部的几个组件进行分析

二、请求分发器 HandleMapping :

2.1 HandleMapping 的概念及其作用:

在 Spring MVC 中,HandlerMapping 是用于决定请求的 URL 应该由哪个处理器(Handler)来处理的组件。

当 DispatcherServlet 接收到一个 HTTP 请求时,它会委托一个或多个 HandlerMapping 来找到适合处理该请求的处理器。它会遍历注册的 HandlerMapping 实现,根据配置和请求的特征来确定最匹配的处理器。

HandlerMapping 提供了多种实现,以适应不同的 URL 映射策略,常见的实现包括:

RequestMappingHandlerMapping:基于注解的 URL 映射,通过扫描带有@RequestMapping 注解的控制器(Handler)类和方法,将它们映射为请求路径。

SimpleUrlHandlerMapping:RouterFunctionMapping是Spring WebFlux框架中的一个关键组件,用于将HTTP请求映射到对应的处理器函数(Handler Function)。

BeanNameUrlHandlerMapping:将请求的 URL 与 bean 的名称进行映射。

通过配置多个 HandlerMapping,可以根据不同的 URL 映射策略来选择适合的处理器。在多个 HandlerMapping 都找不到适合的处理器时,可以根据配置选择做进一步的处理,如显示自定义的错误页面或返回特定的响应。

由此可见HandleMapping 主要功能就是通过对请求的路径解析,然后交由具体HandlerMapping 实现类完成处理的,但是平时我们在项目中,并没有自定义 HandlerMapping ,实际上spring-mvc 中有默认的HandlerMapping 实现可以让我们直接使用;

2.2 HandleMapping 的三种默认实现

2.2.1 HandleMapping 实现的时机:

DispatcherServlet的onRefresh方法在Spring容器刷新(refresh)的过程中被调用。具体的执行时机如下:

  • 当Spring容器启动时,会初始化并加载配置文件,创建并初始化各个bean,并完成依赖注入等操作。
  • 当Spring容器初始化完毕后,会调用DispatcherServlet的init方法进行初始化。
  • 在DispatcherServlet的init方法中,会调用父类FrameworkServlet的initServletBean方法。
  • 在initServletBean方法中,会调用onRefresh方法,用于处理Spring容器刷新的操作。
  • 在onRefresh方法中,会初始化并准备DispatcherServlet所需要的各个组件,包括HandlerMapping、HandlerAdapter、ViewResolver等。
  • 完成组件的初始化后,DispatcherServlet即可接收并处理请求。
  • 需要注意的是,onRefresh方法的执行时机是在Spring容器初始化完毕后,并在DispatcherServlet初始化的过程中调用的。在onRefresh方法执行之前,Spring容器已经完成了bean的创建和初始化操作。

DispatcherServlet 中的 onRefresh 方法:

java 复制代码
protected void onRefresh(ApplicationContext context) {
this.initStrategies(context);
}

protected void initStrategies(ApplicationContext context) {
    this.initMultipartResolver(context);
    this.initLocaleResolver(context);
    this.initThemeResolver(context);
    // HandlerMapping 的初始化
    this.initHandlerMappings(context);
     // HandlerAdapter的初始化
    this.initHandlerAdapters(context);
    this.initHandlerExceptionResolvers(context);
    this.initRequestToViewNameTranslator(context);
    this.initViewResolvers(context);
    this.initFlashMapManager(context);
}

接下来主要对HandlerMapping 默认的三种实现方式进行分析;

2.2.2 RequestMappingHandlerMapping作用 :

它主要是对 @RequestMapping 注解的解析,因为其父类AbstractHandlerMethodMapping 实现了InitializingBean 接口所以在对RequestMappingHandlerMapping 进行依赖注入完成后 会调用 afterPropertiesSet 方法

java 复制代码
 public void afterPropertiesSet() {
	// 初始化一些类型的解析
   this.config = new RequestMappingInfo.BuilderConfiguration();
   this.config.setPatternParser(this.getPathPatternParser());
   this.config.setContentTypeResolver(this.getContentTypeResolver());
   // 扫描解析@RequestMapping 
   super.afterPropertiesSet();
}

调用父类 AbstractHandlerMethodMapping 的 afterPropertiesSet 方法:

java 复制代码
public void afterPropertiesSet() {
     this.initHandlerMethods();
     int total = this.getHandlerMethods().size();
     if (this.logger.isTraceEnabled() && total == 0 || this.logger.isDebugEnabled() && total > 0) {
         this.logger.debug(total + " mappings in " + this.formatMappingName());
     }

 }

 protected void initHandlerMethods() {
 	// 获取spring 容器中所哟的bean
     String[] beanNames = this.obtainApplicationContext().getBeanNamesForType(Object.class);
     String[] var2 = beanNames;
     int var3 = beanNames.length;

     for(int var4 = 0; var4 < var3; ++var4) {
         String beanName = var2[var4];
         if (!beanName.startsWith("scopedTarget.")) {
             Class<?> beanType = null;
			// 遍历每个bean
             try {
             	// 获取bean 的类型
                 beanType = this.obtainApplicationContext().getType(beanName);
             } catch (Throwable var8) {
                 if (this.logger.isTraceEnabled()) {
                     this.logger.trace("Could not resolve type for bean '" + beanName + "'", var8);
                 }
             }
			// 如果改bean 被 Controller 或者  RequestMapping 注解修饰 则为http 请求处理类
             if (beanType != null && this.isHandler(beanType)) {
             	// 对类中的方法进行收集
                 this.detectHandlerMethods(beanName);
             }
         }
     }

     this.handlerMethodsInitialized(this.getHandlerMethods());
 }

其中 this.isHandler 进行当前bean 的类的注解判断:

java 复制代码
protected boolean isHandler(Class<?> beanType) {
	 return AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class);
}

在看下对于类中方法的收集:

java 复制代码
protected void detectHandlerMethods(final Object handler) {
		// 获取bean 的class 类型
        Class<?> handlerType = handler instanceof String ? this.obtainApplicationContext().getType((String)handler) : handler.getClass();
    if (handlerType != null) {
        Class<?> userType = ClassUtils.getUserClass(handlerType);
        // 获取到加了RequestMapping  注解的方法
        Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (method) -> {
            return this.getMappingForMethod(method, userType);
        });
        if (this.logger.isTraceEnabled()) {
            this.logger.trace(this.formatMappings(userType, methods));
        } else if (this.mappingsLogger.isDebugEnabled()) {
            this.mappingsLogger.debug(this.formatMappings(userType, methods));
        }
		// 遍历方法 然后进行注册,方便后续 通过http 请求路径 来筛选 HandlerMapping
        methods.forEach((method, mapping) -> {
            Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
            this.registerHandlerMethod(handler, invocableMethod, mapping);
        });
    }

}

对于类中加了RequestMapping 注解方法的收集:

java 复制代码
 @Nullable
    protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
    	// info 不为null 则说明增加了 RequestMapping   注解
        RequestMappingInfo info = this.createRequestMappingInfo(method);
    if (info != null) {
    	//
         RequestMappingInfo typeInfo = this.createRequestMappingInfo(handlerType);
         if (typeInfo != null) {
             info = typeInfo.combine(info);
         }

         Iterator var5 = this.pathPrefixes.entrySet().iterator();

         while(var5.hasNext()) {
             Map.Entry<String, Predicate<Class<?>>> entry = (Map.Entry)var5.next();
             if (((Predicate)entry.getValue()).test(handlerType)) {
                 String prefix = (String)entry.getKey();
                 if (this.embeddedValueResolver != null) {
                     prefix = this.embeddedValueResolver.resolveStringValue(prefix);
                 }

                 info = RequestMappingInfo.paths(new String[]{prefix}).options(this.config).build().combine(info);
                 break;
             }
         }
     }

     return info;
 }

RequestMappingInfo info 对象的获取:

java 复制代码
 @Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
	// 获取到方法上的RequestMapping 注解对象
    RequestMapping requestMapping = (RequestMapping)AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
    RequestCondition<?> condition = element instanceof Class ? this.getCustomTypeCondition((Class)element) : this.getCustomMethodCondition((Method)element);
    // 有RequestMapping  注解则封装为 RequestMappingInfo  返回,否则直接返回null
    return requestMapping != null ? this.createRequestMappingInfo(requestMapping, condition) : null;
}

2.2.3 BeanNameUrlHandlerMapping 作用:

主要是对bean 的名称以"/" 的进行处理,其父类ApplicationObjectSupport 实现了ApplicationContextAware 接口所以在对BeanNameUrlHandlerMapping 进行初始化之后会调用initApplicationContext 方法:

AbstractDetectingUrlHandlerMapping 的initApplicationContext 方法

java 复制代码
 public void initApplicationContext() throws ApplicationContextException {
	 super.initApplicationContext();
	 // 对bean 进行筛选
     this.detectHandlers();
 }
 protected void detectHandlers() throws BeansException {
    ApplicationContext applicationContext = this.obtainApplicationContext();
    // 获取spring 中所有的bean
    String[] beanNames = this.detectHandlersInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) : applicationContext.getBeanNamesForType(Object.class);
    String[] var3 = beanNames;
    int var4 = beanNames.length;
	// 依次遍历
    for(int var5 = 0; var5 < var4; ++var5) {
        String beanName = var3[var5];
        // 如果bean 的名称 以 / 开头 则进行收集
        String[] urls = this.determineUrlsForHandler(beanName);
        if (!ObjectUtils.isEmpty(urls)) {
        	// registerHandler 对筛选的bean 进行收集:
            this.registerHandler(urls, beanName);
        }
    }

    if (this.mappingsLogger.isDebugEnabled()) {
        this.mappingsLogger.debug(this.formatMappingName() + " " + this.getHandlerMap());
    } else if (this.logger.isDebugEnabled() && !this.getHandlerMap().isEmpty() || this.logger.isTraceEnabled()) {
        this.logger.debug("Detected " + this.getHandlerMap().size() + " mappings in " + this.formatMappingName());
    }

}

determineUrlsForHandler 判断bean:BeanNameUrlHandlerMapping 下的determineUrlsForHandler

java 复制代码
protected String[] determineUrlsForHandler(String beanName) {
    List<String> urls = new ArrayList();
    if (beanName.startsWith("/")) {
    	// bean 的名称以  / 开头
        urls.add(beanName);
    }

    String[] aliases = this.obtainApplicationContext().getAliases(beanName);
    String[] var4 = aliases;
    int var5 = aliases.length;

    for(int var6 = 0; var6 < var5; ++var6) {
        String alias = var4[var6];
        if (alias.startsWith("/")) {
            urls.add(alias);
        }
    }

    return StringUtils.toStringArray(urls);
}

2.2.4 RouterFunctionMapping 作用:

主要 解析 Spring WebFlux 框架中通过RouterFunctions 中绑定关系的解析,因为RouterFunctionMapping 实现了InitializingBean 接口所以在完成对其依赖注入后会调用afterPropertiesSet 方法

java 复制代码
public void afterPropertiesSet() throws Exception {
    if (CollectionUtils.isEmpty(this.messageReaders)) {
        ServerCodecConfigurer codecConfigurer = ServerCodecConfigurer.create();
        this.messageReaders = codecConfigurer.getReaders();
    }

    if (this.routerFunction == null) {
		// 收集routerFunction 
        this.initRouterFunctions();
    }

    if (this.routerFunction != null) {
        RouterFunctions.changeParser(this.routerFunction, this.getPathPatternParser());
    }

}

routerFunction 的收集

java 复制代码
protected void initRouterFunctions() {
	// 获取所有的RouterFunction 类型的bean 
    List<RouterFunction<?>> routerFunctions = this.routerFunctions();
	// 进行合并后赋值给routerFunction  属性 private RouterFunction<?> routerFunction;
    this.routerFunction = (RouterFunction)routerFunctions.stream().reduce(RouterFunction::andOther).orElse((Object)null);
    this.logRouterFunctions(routerFunctions);
}

private List<RouterFunction<?>> routerFunctions() {
    return (List)this.obtainApplicationContext().getBeanProvider(RouterFunction.class).orderedStream().map((router) -> {
        return router;
    }).collect(Collectors.toList());
}

介绍完了HandlerMapping 的三种默认实现,接下来再来看下 另外一个组件HandlerAdapter;

三、HandlerAdapter 介绍接作用 :

3.1 HandlerAdapter 的作用:

HandlerAdapter 是 Spring MVC 框架中的一个关键组件,它的作用是将请求交给合适的处理器进行处理,并对处理结果进行适配,以便正确返回给客户端。

HandlerAdapter 的主要作用包括:

  1. 选择合适的处理器:根据请求的特征和配置的规则,HandlerAdapter 从多个处理器中选择合适的处理器来处理请求。例如,根据请求的URL路径、请求方法、请求参数等进行选择。

  2. 适配处理器的方法:不同的处理器(Controller)可能使用不同的处理方法来处理请求。HandlerAdapter负责将请求适配到处理器的特定方法,并调用该方法处理请求。HandlerAdapter 确保处理器方法的参数正确绑定到请求的数据和上下文信息上。

  3. 处理结果的适配:处理器方法执行完毕后,得到的处理结果可能是一个逻辑视图名、模型对象或者其他类型的对象。HandlerAdapter 负责将处理结果适配成对应的视图对象、响应格式等,以便返回给客户端。

  4. 异常处理:当处理器方法执行过程中发生异常时,HandlerAdapter 负责捕获和处理异常。它可以根据配置的异常处理策略,将异常信息适配成对应的错误响应,并正确返回给客户端。

通过适配不同类型的处理器和处理器方法,HandlerAdapter 将请求和处理器解耦,并提供统一的请求处理流程。它使得开发者可以更加专注于业务逻辑的编写,而无需关注请求的细节和适配处理。

HandlerAdapter 是 Spring MVC 框架中负责选择合适的处理器、适配处理方法并处理结果的关键组件,它实现了请求和处理器的解耦和适配,以实现统一的请求处理流程和结果返回。

HandlerAdapter 是 Spring MVC 框架中的一个关键组件,它的作用是将请求交给合适的处理器进行处理,并对处理结果进行适配,以便正确返回给客户端。

HandlerAdapter 的主要作用包括:

  1. 选择合适的处理器:根据请求的特征和配置的规则,HandlerAdapter 从多个处理器中选择合适的处理器来处理请求。例如,根据请求的URL路径、请求方法、请求参数等进行选择。

  2. 适配处理器的方法:不同的处理器(Controller)可能使用不同的处理方法来处理请求。HandlerAdapter负责将请求适配到处理器的特定方法,并调用该方法处理请求。HandlerAdapter 确保处理器方法的参数正确绑定到请求的数据和上下文信息上。

  3. 处理结果的适配:处理器方法执行完毕后,得到的处理结果可能是一个逻辑视图名、模型对象或者其他类型的对象。HandlerAdapter 负责将处理结果适配成对应的视图对象、响应格式等,以便返回给客户端。

  4. 异常处理:当处理器方法执行过程中发生异常时,HandlerAdapter 负责捕获和处理异常。它可以根据配置的异常处理策略,将异常信息适配成对应的错误响应,并正确返回给客户端。

通过适配不同类型的处理器和处理器方法,HandlerAdapter 将请求和处理器解耦,并提供统一的请求处理流程。它使得开发者可以更加专注于业务逻辑的编写,而无需关注请求的细节和适配处理。

HandlerAdapter 是 Spring MVC 框架中负责选择合适的处理器、适配处理方法并处理结果的关键组件,它实现了请求和处理器的解耦和适配,以实现统一的请求处理流程和结果返回。平常开发中我们也没有自定义 HandlerAdapter 的实现,使用的最多的还是spring-mvc 提供的几种默认实现,下面看下它的几种默认实现:

3.1 HandlerAdapter的几种默认实现:

3.1.1 .HttpRequestHandlerAdapter

HttpRequestHandlerAdapter:用于适配实现了HttpRequestHandler接口的处理器。HttpRequestHandler是一种比较底层的处理器类型,需要开发者自己实现处理逻辑。HttpRequestHandlerAdapter负责调用其handleRequest()方法执行逻辑。

3.1.2 .SimpleControllerHandlerAdapter

SimpleControllerHandlerAdapter:用于适配实现了Controller接口的处理器。Controller是一种较早版本的处理器类型,需要实现handleRequest()方法,返回一个ModelAndView对象。SimpleControllerHandlerAdapter将调用处理器的handleRequest()方法,并将其返回的ModelAndView适配成所需的视图结果。

3.1.3 .RequestMappingHandlerAdapter

RequestMappingHandlerAdapter:用于适配带有@RequestMapping注解的Controller类。它支持将请求参数绑定到方法参数、处理方法返回值的适配,并负责调用处理方法执行请求处理逻辑。

3.1.4 .HandlerFunctionAdapter

HandlerFunctionAdapter 是 Spring WebFlux 框架中的一个默认 HandlerAdapter 实现,用于适配 HandlerFunction。HandlerFunction 是一种用于处理请求的函数式接口,它接收一个 ServerRequest 对象,返回一个 Mono 对象,用于表示异步的响应结果。


总结

DispatcherServlet 的初始化主要是对请求分布的bean HandlerMapping 和负责请求参数解析,方法调用及方法返回值解析的HandlerAdapter 组成,后续的文章会分析 HandlerAdapter 对于请求方法的参数解析,方法的调用,以及返回值的包装返回;

相关推荐
码上一元2 小时前
SpringBoot自动装配原理解析
java·spring boot·后端
计算机-秋大田2 小时前
基于微信小程序的养老院管理系统的设计与实现,LW+源码+讲解
java·spring boot·微信小程序·小程序·vue
魔道不误砍柴功4 小时前
简单叙述 Spring Boot 启动过程
java·数据库·spring boot
失落的香蕉4 小时前
C语言串讲-2之指针和结构体
java·c语言·开发语言
枫叶_v4 小时前
【SpringBoot】22 Txt、Csv文件的读取和写入
java·spring boot·后端
wclass-zhengge4 小时前
SpringCloud篇(配置中心 - Nacos)
java·spring·spring cloud
路在脚下@4 小时前
Springboot 的Servlet Web 应用、响应式 Web 应用(Reactive)以及非 Web 应用(None)的特点和适用场景
java·spring boot·servlet
黑马师兄4 小时前
SpringBoot
java·spring
数据小小爬虫4 小时前
如何用Java爬虫“偷窥”淘宝商品类目API的返回值
java·爬虫·php
暮春二十四4 小时前
关于用postman调用接口成功但是使用Java代码调用却失败的问题
java·测试工具·postman