我们知道一个http 请求到达服务端后是通过DispatcherServlet 进行请求的分发,而分发的逻辑具体就是要找到这个请求合适的HandleMapping 来处理,那么HandleMapping 是什么,它是怎么根据请求路径判断 哪个HandleMapping 来处理请求呢,HandleMapping 分发请求后,怎么完成对方法参数的解析,又是如何将方法的返回值给封装成浏览器需要的对象?
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 请求的全部流程,想要知晓它的工作情况,就需要对它内部的几个组件进行分析
在 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 实现可以让我们直接使用;
DispatcherServlet的onRefresh方法在Spring容器刷新(refresh)的过程中被调用。具体的执行时机如下:
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 默认的三种实现方式进行分析;
它主要是对 @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 容器中所哟的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 的类的注解判断:
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 请求路径 来筛选 HandlerMapping
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
this.registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
对于类中加了RequestMapping 注解方法的收集:
@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 对象的获取:
@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;
}
主要是对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 中所有的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
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);
}
主要 解析 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 是 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 提供的几种默认实现,下面看下它的几种默认实现:
HttpRequestHandlerAdapter:用于适配实现了HttpRequestHandler接口的处理器。HttpRequestHandler是一种比较底层的处理器类型,需要开发者自己实现处理逻辑。HttpRequestHandlerAdapter负责调用其handleRequest()方法执行逻辑。
SimpleControllerHandlerAdapter:用于适配实现了Controller接口的处理器。Controller是一种较早版本的处理器类型,需要实现handleRequest()方法,返回一个ModelAndView对象。SimpleControllerHandlerAdapter将调用处理器的handleRequest()方法,并将其返回的ModelAndView适配成所需的视图结果。
RequestMappingHandlerAdapter:用于适配带有@RequestMapping注解的Controller类。它支持将请求参数绑定到方法参数、处理方法返回值的适配,并负责调用处理方法执行请求处理逻辑。
HandlerFunctionAdapter 是 Spring WebFlux 框架中的一个默认 HandlerAdapter 实现,用于适配 HandlerFunction。HandlerFunction 是一种用于处理请求的函数式接口,它接收一个 ServerRequest 对象,返回一个 Mono 对象,用于表示异步的响应结果。
DispatcherServlet 的初始化主要是对请求分布的bean HandlerMapping 和负责请求参数解析,方法调用及方法返回值解析的HandlerAdapter 组成,后续的文章会分析 HandlerAdapter 对于请求方法的参数解析,方法的调用,以及返回值的包装返回;