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 对于请求方法的参数解析,方法的调用,以及返回值的包装返回;

相关推荐
考虑考虑3 小时前
Jpa使用union all
java·spring boot·后端
用户3721574261354 小时前
Java 实现 Excel 与 TXT 文本高效互转
java
浮游本尊5 小时前
Java学习第22天 - 云原生与容器化
java
渣哥6 小时前
原来 Java 里线程安全集合有这么多种
java
间彧7 小时前
Spring Boot集成Spring Security完整指南
java
间彧7 小时前
Spring Secutiy基本原理及工作流程
java
Java水解8 小时前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
洛小豆10 小时前
在Java中,Integer.parseInt和Integer.valueOf有什么区别
java·后端·面试
前端小张同学10 小时前
服务器上如何搭建jenkins 服务CI/CD😎😎
java·后端
ytadpole11 小时前
Spring Cloud Gateway:一次不规范 URL 引发的路由转发404问题排查
java·后端