文章目录
- 前言
- 一、DispatcherServlet 概念以及作用:
- 二、请求分发器 HandleMapping :
- 2.1 HandleMapping 的概念及其作用:
- 2.2 HandleMapping 的三种默认实现
- 2.2.1 HandleMapping 实现的时机:
- 2.2.2 RequestMappingHandlerMapping作用 :
- 2.2.3 BeanNameUrlHandlerMapping 作用:
- 2.2.4 RouterFunctionMapping 作用:
- 三、HandlerAdapter 介绍接作用 :
- 3.1 HandlerAdapter 的作用:
- 3.1 HandlerAdapter的几种默认实现:
- 3.1.1 .HttpRequestHandlerAdapter
- 3.1.2 .SimpleControllerHandlerAdapter
- 3.1.3 .RequestMappingHandlerAdapter
- 3.1.4 .HandlerFunctionAdapter
- 总结
前言
我们知道一个http 请求到达服务端后是通过DispatcherServlet 进行请求的分发,而分发的逻辑具体就是要找到这个请求合适的HandleMapping 来处理,那么HandleMapping 是什么,它是怎么根据请求路径判断 哪个HandleMapping 来处理请求呢,HandleMapping 分发请求后,怎么完成对方法参数的解析,又是如何将方法的返回值给封装成浏览器需要的对象?
一、DispatcherServlet 概念以及作用:
DispatcherServlet 是 Spring MVC 框架的核心组件之一,它是一个前端控制器(Front Controller)。
DispatcherServlet 充当了统一的请求处理器和请求调度中心的角色。当一个 HTTP 请求发起到应用程序时,它首先被 DispatcherServlet 接收并处理。DispatcherServlet 根据请求的特征和配置的规则,将请求分配给合适的处理器来进行处理,并将处理结果返回给客户端。
DispatcherServlet 的主要功能包括:
-
请求的接收和解析:DispatcherServlet 接收并解析 HTTP 请求,包括请求的 URL、参数、头信息等。
-
HandlerMapping:DispatcherServlet 使用一个或多个 HandlerMapping 来决定请求应该由哪个处理器来处理。HandlerMapping 根据请求的 URL 和其他条件,将请求映射到相应的处理器(即控制器)。
-
HandlerAdapter:DispatcherServlet 使用一个或多个 HandlerAdapter 来适配不同类型的处理器。HandlerAdapter 将处理器适配成统一的处理器方法的调用方式,以便进行统一的处理和返回结果。
-
视图解析和渲染:DispatcherServlet 使用视图解析器(ViewResolver)来解析处理器返回的逻辑视图名,并将其渲染为最终的响应结果。可以是HTML、JSON、XML等不同格式的响应。
-
异常处理和错误页面: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 方法:
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 方法
public void afterPropertiesSet() {// 初始化一些类型的解析this.config = new RequestMappingInfo.BuilderConfiguration();this.config.setPatternParser(this.getPathPatternParser());this.config.setContentTypeResolver(this.getContentTypeResolver());// 扫描解析@RequestMapping super.afterPropertiesSet();
}
调用父类 AbstractHandlerMethodMapping 的 afterPropertiesSet 方法:
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 容器中所哟的beanString[] 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;// 遍历每个beantry {// 获取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 的类的注解判断:
protected boolean isHandler(Class<?> beanType) {return AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class);
}
在看下对于类中方法的收集:
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 请求路径 来筛选 HandlerMappingmethods.forEach((method, mapping) -> {Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);this.registerHandlerMethod(handler, invocableMethod, mapping);});}}
对于类中加了RequestMapping 注解方法的收集:
@Nullableprotected 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 对象的获取:
@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 返回,否则直接返回nullreturn requestMapping != null ? this.createRequestMappingInfo(requestMapping, condition) : null;
}
2.2.3 BeanNameUrlHandlerMapping 作用:
主要是对bean 的名称以"/" 的进行处理,其父类ApplicationObjectSupport 实现了ApplicationContextAware 接口所以在对BeanNameUrlHandlerMapping 进行初始化之后会调用initApplicationContext 方法:
AbstractDetectingUrlHandlerMapping 的initApplicationContext 方法
public void initApplicationContext() throws ApplicationContextException {super.initApplicationContext();// 对bean 进行筛选this.detectHandlers();}protected void detectHandlers() throws BeansException {ApplicationContext applicationContext = this.obtainApplicationContext();// 获取spring 中所有的beanString[] 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
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 方法
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 的收集
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 的主要作用包括:
-
选择合适的处理器:根据请求的特征和配置的规则,HandlerAdapter 从多个处理器中选择合适的处理器来处理请求。例如,根据请求的URL路径、请求方法、请求参数等进行选择。
-
适配处理器的方法:不同的处理器(Controller)可能使用不同的处理方法来处理请求。HandlerAdapter负责将请求适配到处理器的特定方法,并调用该方法处理请求。HandlerAdapter 确保处理器方法的参数正确绑定到请求的数据和上下文信息上。
-
处理结果的适配:处理器方法执行完毕后,得到的处理结果可能是一个逻辑视图名、模型对象或者其他类型的对象。HandlerAdapter 负责将处理结果适配成对应的视图对象、响应格式等,以便返回给客户端。
-
异常处理:当处理器方法执行过程中发生异常时,HandlerAdapter 负责捕获和处理异常。它可以根据配置的异常处理策略,将异常信息适配成对应的错误响应,并正确返回给客户端。
通过适配不同类型的处理器和处理器方法,HandlerAdapter 将请求和处理器解耦,并提供统一的请求处理流程。它使得开发者可以更加专注于业务逻辑的编写,而无需关注请求的细节和适配处理。
HandlerAdapter 是 Spring MVC 框架中负责选择合适的处理器、适配处理方法并处理结果的关键组件,它实现了请求和处理器的解耦和适配,以实现统一的请求处理流程和结果返回。
HandlerAdapter 是 Spring MVC 框架中的一个关键组件,它的作用是将请求交给合适的处理器进行处理,并对处理结果进行适配,以便正确返回给客户端。
HandlerAdapter 的主要作用包括:
-
选择合适的处理器:根据请求的特征和配置的规则,HandlerAdapter 从多个处理器中选择合适的处理器来处理请求。例如,根据请求的URL路径、请求方法、请求参数等进行选择。
-
适配处理器的方法:不同的处理器(Controller)可能使用不同的处理方法来处理请求。HandlerAdapter负责将请求适配到处理器的特定方法,并调用该方法处理请求。HandlerAdapter 确保处理器方法的参数正确绑定到请求的数据和上下文信息上。
-
处理结果的适配:处理器方法执行完毕后,得到的处理结果可能是一个逻辑视图名、模型对象或者其他类型的对象。HandlerAdapter 负责将处理结果适配成对应的视图对象、响应格式等,以便返回给客户端。
-
异常处理:当处理器方法执行过程中发生异常时,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 对于请求方法的参数解析,方法的调用,以及返回值的包装返回;