SpringMVC扩展点

SpringMVC扩展点

SpringMVC执行流程

image-20240328171342686

  1. DispatchServlet:
    • 这是Spring MVC的核心控制器,负责处理所有的HTTP请求和响应。它根据请求的URL来决定调用哪个处理器(Controller中的方法),并最终返回响应给客户端。
    • 它使用HandlerMapping来确定具体的请求应该由哪个处理器方法来处理。
  2. HandlerMapping:
    • 这个接口定义了如何将HTTP请求映射到处理器方法。RequestMappingHandlerMappingHandlerMapping的一个实现,它使用注解(如@RequestMapping)来映射URL到具体的处理器方法。
  3. HandlerAdapter:
    • 这个接口定义了如何调用处理器方法。RequestMappingHandlerAdapterHandlerAdapter的一个实现,它负责调用与@RequestMapping注解的URL匹配的处理器方法。
  4. RequestMappingHandlerMappingRequestMappingHandlerAdapter:
    • 这两个组件是Spring MVC中处理基于注解的映射的核心。RequestMappingHandlerMapping负责查找和匹配URL到相应的处理器方法,而RequestMappingHandlerAdapter负责实际调用这些方法。
  5. MappingRegistry:
    • 这是一个注册表,用于存储URL模式和对应的处理器方法。它使得RequestMappingHandlerMapping能够快速查找匹配的处理器方法。
  6. HandlerMethodArgumentResolverHandlerMethodReturnValueHandler:
    • 这两个接口定义了如何处理进入和返回自处理器方法的数据。HandlerMethodArgumentResolver用于解析方法参数,例如将URL中的占位符(如{id})转换为方法参数的值。
    • HandlerMethodReturnValueHandler用于处理方法的返回值,将其转换为适合HTTP响应的形式。
  7. PathVariableMethodArgumentResolverRequestResponseBodyMethodProcessor:
    • 这些是具体的解析器和处理器,用于处理特定的数据类型。PathVariableMethodArgumentResolver用于解析URL路径变量,而RequestResponseBodyMethodProcessor用于处理请求体和响应体,例如将JSON或XML数据绑定到Java对象,或将Java对象序列化为JSON或XML。
  8. MappingJackson2HttpMessageConverter:
    • 这是一个消息转换器,用于将HTTP请求和响应体与Java对象之间进行转换。它通常用于处理JSON数据,将JSON字符串转换为Java对象,或将Java对象转换为JSON字符串。
  9. MultiValueMap<String, T>:
    • 这是一个泛型接口,用于表示HTTP请求中参数的映射,其中键是参数名,值是参数值的列表。这允许处理具有相同名称的多个参数的情况。

这些组件通过它们的接口和实现类相互协作,形成了Spring MVC的请求处理流程。当一个HTTP请求到达时,DispatchServlet会使用HandlerMapping来确定请求应该由哪个处理器方法处理,然后通过HandlerAdapter来调用这个方法。参数和返回值的处理由HandlerMethodArgumentResolverHandlerMethodReturnValueHandler负责。最终,请求被正确地处理并返回给客户端。

一般需要扩展的地方包括:

  • HandlerMethodArgumentResolver
    可以定制自己的参数接收方式 和@PathVariable同级
  • HandlerMethodReturnValueHandler
    定制返回方式,和@ResponseBody同级
  • MessageConverter
    如果采用@ResponseBody,可以根据不同的ClassMediaType定制不同的Converter

HandlerMethodArgumentResolver

HandlerMethodArgumentResolver接口是Spring MVC中用于解析和提供处理器方法参数的组件。如果你想自定义参数的接收方式,可以通过实现这个接口来创建自己的参数解析器。下面是一个简单的代码示例,展示了如何创建一个自定义的HandlerMethodArgumentResolver来解析请求头中的参数:

import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.bind.support.HandlerMethodArgumentResolver;
import javax.servlet.http.HttpServletRequest;

public class CustomHeaderArgumentResolver implements HandlerMethodArgumentResolver {

private static final String CUSTOM_HEADER_NAME = "X-Custom-Header";

@Override
public boolean supportsParameter(MethodParameter parameter) {
// 检查方法参数是否有CustomHeader注解
return parameter.getParameterAnnotation(CustomHeader.class) != null;
}

@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
// 从请求头中获取自定义的参数值
String headerValue = request.getHeader(CUSTOM_HEADER_NAME);
// 根据参数类型进行转换,这里假设是String类型
return headerValue;
}
}

在这个示例中,我们创建了一个名为CustomHeaderArgumentResolver的类,它实现了HandlerMethodArgumentResolver接口。我们定义了一个名为CUSTOM_HEADER_NAME的常量来指定我们要解析的请求头的名称。

supportsParameter方法检查方法参数是否有CustomHeader注解。这是一个自定义注解,你可以创建它来标记需要从请求头中解析的参数。

resolveArgument方法是实际解析参数的地方。我们从NativeWebRequest中获取HttpServletRequest对象,然后调用getHeader方法来获取请求头中的值。最后,我们将这个值返回给Spring MVC,它将把这个值作为参数传递给处理器方法。

为了使用这个自定义的参数解析器,你需要将其添加到Spring MVC的解析器列表中。这通常在Spring配置文件中完成,如下所示:

@Configuration
public class WebConfig implements WebMvcConfigurer {

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new CustomHeaderArgumentResolver());
}
}

现在,当你在控制器方法中使用@CustomHeader注解标记的参数时,Spring MVC将会使用我们自定义的CustomHeaderArgumentResolver来解析这个参数。

@RestController
public class MyController {

@GetMapping("/example")
public String exampleMethod(@CustomHeader String customHeaderValue) {
// 使用从请求头中解析的参数
return "Custom header value: " + customHeaderValue;
}
}

在这个控制器方法中,customHeaderValue参数将通过我们自定义的HandlerMethodArgumentResolver来获取请求头中X-Custom-Header的值。

HandlerMethodReturnValueHandler

HandlerMethodReturnValueHandler接口允许开发者自定义Spring MVC处理器方法的返回值处理逻辑。以下是一个简单的代码示例,展示了如何实现这个接口来自定义返回值的处理:

首先,我们创建一个自定义的HandlerMethodReturnValueHandler实现类,用于处理返回值为String类型的方法:

import org.springframework.core.MethodParameter;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

public class CustomStringReturnValueHandler implements ResponseBodyAdvice<Object> {

@Override
public boolean supports(MethodParameter returnType, Class<? extends ResponseBodyAdvice<?>> converterType) {
// 检查返回类型是否为String
return String.class.isAssignableFrom(returnType.getParameterType());
}

@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
NativeWebRequest request, ModelAndViewContainer mavContainer) throws Exception {
// 自定义返回值的处理逻辑,例如添加额外的信息
if (body instanceof String) {
String responseBody = (String) body;
return "Custom String: " + responseBody;
}
return body;
}
}

在这个实现中,supports方法检查返回类型是否为String。如果是,那么这个HandlerMethodReturnValueHandler将被用于处理返回值。

beforeBodyWrite方法是实际处理返回值的地方。在这个方法中,我们可以自定义返回值的处理逻辑。在这个例子中,我们检查返回的body是否为String类型,如果是,我们添加了一些自定义的前缀文本。

接下来,我们需要将这个自定义的HandlerMethodReturnValueHandler注册到Spring MVC中:

import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@Configuration
public class WebConfig implements WebMvcConfigurer {

@Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
handlers.add(new CustomStringReturnValueHandler());
}
}

WebConfig配置类中,我们通过实现WebMvcConfigurer接口并重写addReturnValueHandlers方法,将我们的CustomStringReturnValueHandler添加到Spring MVC的返回值处理器列表中。

现在,当控制器方法返回一个String类型的值时,我们的自定义HandlerMethodReturnValueHandler将会被调用来处理这个返回值:

@RestController
public class MyController {

@GetMapping("/example")
public String exampleMethod() {
// 控制器方法返回一个字符串
return "Hello, World!";
}
}

在这个例子中,exampleMethod方法返回的字符串”Hello, World!”将会被CustomStringReturnValueHandler处理,最终响应给客户端的内容将是”Custom String: Hello, World!”。这样,我们就成功地自定义了返回值的处理逻辑。

MessageConverter

MessageConverter接口是Spring MVC中用于在请求和响应之间转换数据格式的组件。以下是一个简单的代码示例,展示了如何实现MessageConverter接口来创建一个自定义的消息转换器,它将自定义的MyObject类实例转换为JSON字符串,并从JSON字符串中反序列化出MyObject实例。

首先,我们定义一个简单的MyObject类,它将被序列化和反序列化:

public class MyObject {
private String name;
private int age;

// 构造函数、getter和setter省略
}

接下来,我们创建一个自定义的MessageConverter实现类:

import org.springframework.http.MediaType;
import org.springframework.http.converter.GenericHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.context.request.RequestContext;

import java.io.IOException;
import java.nio.charset.Charset;

public class CustomMyObjectConverter extends MappingJackson2HttpMessageConverter {

// 定义支持的媒体类型
public static final MediaType MY_OBJECT_MEDIA_TYPE = new MediaType("application", "x-my-object");

@Override
public boolean supports(Class<?> clazz, MediaType mediaType) {
// 检查类类型和媒体类型
return MyObject.class.isAssignableFrom(clazz) && MY_OBJECT_MEDIA_TYPE.isCompatibleWith(mediaType);
}

@Override
protected boolean supportsInternal(MediaType mediaType) {
// 检查媒体类型是否与自定义类型匹配
return MY_OBJECT_MEDIA_TYPE.isCompatibleWith(mediaType);
}

@Override
public Object readInternal(Class<?> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
// 调用父类方法进行JSON读取和反序列化
return super.readInternal(clazz, inputMessage);
}

@Override
protected void writeInternal(Object object, MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
// 调用父类方法进行序列化和JSON写入
super.writeInternal(object, contentType, outputMessage);
}

@Override
public List<MediaType> getSupportedMediaTypes() {
// 返回支持的媒体类型列表
return Collections.singletonList(MY_OBJECT_MEDIA_TYPE);
}
}

在这个实现中,我们继承了MappingJackson2HttpMessageConverter,它是一个基于Jackson库的JSON消息转换器。我们重写了supports方法来指定我们的转换器只处理MyObject类型的类,并且只支持我们自定义的MY_OBJECT_MEDIA_TYPE媒体类型。

我们还重写了readInternalwriteInternal方法,虽然在这个例子中我们只是调用了父类的方法,但这些方法是你可以自定义序列化和反序列化逻辑的地方。

最后,我们重写了getSupportedMediaTypes方法来返回我们的转换器支持的媒体类型列表。

为了注册我们的自定义MessageConverter,我们需要在Spring配置中添加它:

import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@Configuration
public class WebConfig implements WebMvcConfigurer {

@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false)
.ignoreAcceptHeader(true)
.defaultContentType(MediaType.TEXT_HTML)
.mediaType("myobject", new CustomMyObjectConverter());
}

@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new CustomMyObjectConverter());
}
}

在这个配置类中,我们通过实现WebMvcConfigurer接口并重写configureContentNegotiationextendMessageConverters方法,将我们的CustomMyObjectConverter添加到Spring MVC的消息转换器列表中,并配置了内容协商。

现在,当控制器方法返回一个MyObject类型的值,并且请求的Accept头或URL路径扩展名指定了application/x-my-object类型时,Spring MVC将使用我们的自定义MessageConverter来处理这个返回值。同样,当客户端发送application/x-my-object类型的请求体时,我们的转换器也会被用来解析请求体中的MyObject实例。

springMVC 扩展点 - 刃牙 - 博客园 (cnblogs.com)

SpringMVC中HandlerMapping和HandlerAdapter详解(适配器模式)_handlermapping和handler adatper-CSDN博客