IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> SpringBoot2整合SpringSecurity+Swagger3(源码分析六) -> 正文阅读

[Java知识库]SpringBoot2整合SpringSecurity+Swagger3(源码分析六)


SpringBoot2整合SpringSecurity+Swagger3系列


SpringBoot2整合Swagger3主要是引入了以下依赖

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-boot-starter</artifactId>
    <version>3.0.0</version>
</dependency>

这个starter除了引入相关依赖之外,最关键的还是自动配置功能了。直接查找包下面的spring.factories文件。这里会引入OpenApiAutoConfiguration
在这里插入图片描述
OpenApiAutoConfiguration类的定义如下所示

@Configuration
@EnableConfigurationProperties(SpringfoxConfigurationProperties.class)
@ConditionalOnProperty(value = "springfox.documentation.enabled", havingValue = "true", matchIfMissing = true)
@Import({
    OpenApiDocumentationConfiguration.class,
    SpringDataRestConfiguration.class,
    BeanValidatorPluginsConfiguration.class,
    Swagger2DocumentationConfiguration.class,
    SwaggerUiWebFluxConfiguration.class,
    SwaggerUiWebMvcConfiguration.class
})
@AutoConfigureAfter({ WebMvcAutoConfiguration.class, JacksonAutoConfiguration.class,
    HttpMessageConvertersAutoConfiguration.class, RepositoryRestMvcAutoConfiguration.class })
public class OpenApiAutoConfiguration {

}

可以看到这里又引入了另外6个配置类。不妨先通过curl -X GET "http://localhost:8080/actuator/beans" -H "accept: application/json" -H "Content-Type: application/json"curl -X GET "http://localhost:8080/actuator/conditions" -H "accept: application/json" -H "Content-Type: application/json"查看SpringBoot的注入情况。当然也可以通过swagger来操作。
在这里插入图片描述
然后在对应的结果当中查询OpenApiAutoConfiguration.结果分别如下

"OpenApiAutoConfiguration": [
  {
	"condition": "OnPropertyCondition",
	"message": "@ConditionalOnProperty (springfox.documentation.enabled=true) matched"
  }
]
"springfox.boot.starter.autoconfigure.OpenApiAutoConfiguration": {
  "aliases": [],
  "scope": "singleton",
  "type": "springfox.boot.starter.autoconfigure.OpenApiAutoConfiguration$$EnhancerBySpringCGLIB$$86e56385",
  "resource": null,
  "dependencies": []
},

不难看出OpenApiAutoConfiguration因为满足了@ConditionalOnProperty条件,然后注册为一个单例Bean,并且最后通过CGLIB进行了代理。
然后接下来分别查看OpenApiAutoConfiguration引入的六个自动配置类。其中SpringDataRestConfiguration、BeanValidatorPluginsConfiguration、SwaggerUiWebFluxConfiguration因为以下不匹配原因最后没有自动配置。

"SpringDataRestConfiguration": {
  "notMatched": [
    {
      "condition": "OnClassCondition",
      "message": "@ConditionalOnClass did not find required class 'org.springframework.data.rest.core.config.RepositoryRestConfiguration'"
    }
  ],
  "matched": []
},
"BeanValidatorPluginsConfiguration": [
  {
    "condition": "OnClassCondition",
    "message": "@ConditionalOnClass found required class 'javax.validation.executable.ExecutableValidator'"
  }
],
"SwaggerUiWebFluxConfiguration": {
  "notMatched": [
    {
      "condition": "OnWebApplicationCondition",
      "message": "did not find reactive web application classes"
    }
  ],
  "matched": []
},

所以这里只需要关注OpenApiDocumentationConfiguration、Swagger2DocumentationConfiguration和SwaggerUiWebMvcConfiguration三个配置类。
结果如下

"springfox.documentation.oas.configuration.OpenApiDocumentationConfiguration": {
  "aliases": [],
  "scope": "singleton",
  "type": "springfox.documentation.oas.configuration.OpenApiDocumentationConfiguration$$EnhancerBySpringCGLIB$$c1e3818f",
  "resource": null,
  "dependencies": []
},

"springfox.documentation.swagger2.configuration.Swagger2DocumentationConfiguration": {
  "aliases": [],
  "scope": "singleton",
  "type": "springfox.documentation.swagger2.configuration.Swagger2DocumentationConfiguration$$EnhancerBySpringCGLIB$$4604bbea",
  "resource": null,
  "dependencies": []
},

"springfox.boot.starter.autoconfigure.SwaggerUiWebMvcConfiguration": {
  "aliases": [],
  "scope": "singleton",
  "type": "springfox.boot.starter.autoconfigure.SwaggerUiWebMvcConfiguration$$EnhancerBySpringCGLIB$$190c0b00",
  "resource": null,
  "dependencies": []
},

从beans的结果中搜索以上结果,同时还能从beans中搜索到以下的结果,而这些结果都是对应的配置类引入的bean。比如在Swagger2DocumentationConfiguration引入了swagger2Module、在SwaggerUiWebMvcConfiguration引入了swaggerUiConfigurer。

"swagger2Module": {
  "aliases": [],
  "scope": "singleton",
  "type": "springfox.documentation.swagger2.configuration.Swagger2JacksonModule",
  "resource": "class path resource [springfox/documentation/swagger2/configuration/Swagger2DocumentationConfiguration.class]",
  "dependencies": []
},

"swaggerUiConfigurer": {
  "aliases": [],
  "scope": "singleton",
  "type": "springfox.boot.starter.autoconfigure.SwaggerUiWebMvcConfigurer",
  "resource": "class path resource [springfox/boot/starter/autoconfigure/SwaggerUiWebMvcConfiguration.class]",
  "dependencies": []
},

当然了,是不是这些配置类就只引入了以上的这些bean呢,当然不是,还是得从bean的定义来看看。比如OpenApiDocumentationConfiguration看起来没有引入其他bean,但是看到它的定义如下

@Configuration
@Import({
    SpringfoxWebConfiguration.class,
    SpringfoxWebMvcConfiguration.class,
    SpringfoxWebFluxConfiguration.class,
    SwaggerCommonConfiguration.class,
    OpenApiMappingConfiguration.class,
    OpenApiWebMvcConfiguration.class,
    OpenApiWebFluxConfiguration.class
})
@ComponentScan(basePackages = {
    "springfox.documentation.oas.web",
    "springfox.documentation.oas.mappers"
})
public class OpenApiDocumentationConfiguration {
}

很明显,这里通过@Import又引入了一堆的自动配置类,而且还有ComponentScan扫描的一些包。对于自动配置类采取以上同样的方式进行分析,最后的结果图如下所示

OpenApiAutoConfiguration:
    OpenApiDocumentationConfiguration:
		- SpringfoxWebConfiguration:
			- JsonSerializer[jsonSerializer](依赖openApiModule、swagger2Module):注册Module到ObjectMapper当中,序列化 
			- DefaultPathProvider[pathProvider]:处理springfox请求路径
			- DescriptionResolver[descriptionResolver]
			- Defaults[defaults]
			- HandlerMethodResolver[methodResolver](依赖typeResolver):处理Spring Web的HandlerMethod
			- DocumentationCache[resourceGroupCache]:Documentation缓存	
			- 扫描springfox.documentation.spring.web.scanners
			- 扫描springfox.documentation.spring.web.readers.operation
			- 扫描springfox.documentation.spring.web.readers.parameter
			- 扫描springfox.documentation.spring.web.plugins
			- 扫描springfox.documentation.spring.web.paths
			- ModelsConfiguration:
				- TypeResolver[typeResolver]:处理泛型类型
		- SpringfoxWebMvcConfiguration:
			- WebMvcObjectMapperConfigurer[webMvcObjectMapperConfigurer]:Spring后置处理器,设置messageConverters
		- SwaggerCommonConfiguration:
			- 扫描springfox.documentation.swagger.schema
			- 扫描springfox.documentation.swagger.readers
			- 扫描springfox.documentation.swagger.web
		- OpenApiMappingConfiguration:
			- OpenApiJacksonModule[openApiModule]:用于JackSon序列化
		- OpenApiWebMvcConfiguration
			- WebMvcBasePathAndHostnameTransformationFilter[webMvcOpenApiTransformer]:请求路径处理
    Swagger2DocumentationConfiguration:
		- JacksonModuleRegistrar[swagger2Module]:用于JackSon序列化
		- SwaggerCommonConfiguration{上面已包含}
		- SpringfoxWebMvcConfiguration{上面已包含}
		- Swagger2WebMvcConfiguration: 
			- WebMvcBasePathAndHostnameTransformationFilter[webMvcSwaggerTransformer]:请求路径处理
    SwaggerUiWebMvcConfiguration:
		- SwaggerUiWebMvcConfigurer[swaggerUiConfigurer]:添加资源处理器以及ViewController

从以上这些Bean来看,主要分为请求路径处理和序列化处理。其中SwaggerUiWebMvcConfigurer定义如下

public class SwaggerUiWebMvcConfigurer implements WebMvcConfigurer {
  private final String baseUrl;

  public SwaggerUiWebMvcConfigurer(String baseUrl) {
    this.baseUrl = baseUrl;
  }

  @Override
  public void addResourceHandlers(ResourceHandlerRegistry registry) {
    String baseUrl = StringUtils.trimTrailingCharacter(this.baseUrl, '/');
    registry.
        addResourceHandler(baseUrl + "/swagger-ui/**")
        .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/")
        .resourceChain(false);
  }

  @Override
  public void addViewControllers(ViewControllerRegistry registry) {
    registry.addViewController(baseUrl + "/swagger-ui/")
        .setViewName("forward:" + baseUrl + "/swagger-ui/index.html");
  }
}

其中WebMvcConfigurer属于Spring Web中的接口。在Spring Boot官方文档当中关于这个接口的使用说明如下
在这里插入图片描述
那么什么是Spring MVC的自动配置呢?其实在OpenApiAutoConfiguration类定义中还有以下一段配置,这个AutoConfigureAfter的意思就是OpenApiAutoConfiguration配置起作用之前必须先处理WebMvcAutoConfiguration这一些。

@AutoConfigureAfter({ WebMvcAutoConfiguration.class, JacksonAutoConfiguration.class,
    HttpMessageConvertersAutoConfiguration.class, RepositoryRestMvcAutoConfiguration.class })
public class OpenApiAutoConfiguration {

}

按照前面查看bean和conditions的方式,结果如下

WebMvcAutoConfiguration:
  - OrderedFormContentFilter[formContentFilter]:解析 HTTP PUT、PATCH 和 DELETE 请求的表单数据并将其公开为 Servlet 请求参数的过滤器。 默认情况下,Servlet 规范仅对 HTTP POST 要求这样做。
  - OrderedHiddenHttpMethodFilter[hiddenHttpMethodFilter]
  - EnableWebMvcConfiguration:
  	  - HandlerMapping[defaultServletHandlerMapping]
      - SimpleUrlHandlerMapping[viewControllerHandlerMapping]
      - SimpleUrlHandlerMapping[resourceHandlerMapping]
      - WelcomePageHandlerMapping[welcomePageHandlerMapping]
      - BeanNameUrlHandlerMapping[beanNameHandlerMapping]
      - RequestMappingHandlerMapping[requestMappingHandlerMapping]
      - RequestMappingHandlerAdapter[requestMappingHandlerAdapter]
      - HttpRequestHandlerAdapter[httpRequestHandlerAdapter]
      - SimpleControllerHandlerAdapter[simpleControllerHandlerAdapter]
      - ValidatorAdapter[mvcValidator]
      - ResourceUrlProvider[mvcResourceUrlProvider]
      - WebConversionService[mvcConversionService]
      - AntPathMatcher[mvcPathMatcher]
      - HandlerExceptionResolverComposite[handlerExceptionResolver]
      - ViewResolverComposite[mvcViewResolver]
      - CompositeUriComponentsContributor[mvcUriComponentsContributor]
      - UrlPathHelper[mvcUrlPathHelper]
      - ContentNegotiationManager[mvcContentNegotiationManager]
  - WebMvcAutoConfigurationAdapter:
      - ContentNegotiatingViewResolver[viewResolver]
      - InternalResourceViewResolver[defaultViewResolver]
      - OrderedRequestContextFilter[requestContextFilter]
      - FaviconConfiguration:
          - ResourceHttpRequestHandler[faviconRequestHandler]
          - SimpleUrlHandlerMapping[faviconHandlerMapping]
JacksonAutoConfiguration:
  - Jackson2ObjectMapperBuilderCustomizerConfiguration:
      - StandardJackson2ObjectMapperBuilderCustomizer[standardJacksonObjectMapperBuilderCustomizer]
  - ParameterNamesModuleConfiguration:
      - ParameterNamesModule[parameterNamesModule]
  - JacksonObjectMapperConfiguration:
      - ObjectMapper[jacksonObjectMapper]
  - JacksonObjectMapperBuilderConfiguration:
      - Jackson2ObjectMapperBuilder[jacksonObjectMapperBuilder]
  - JsonComponentModule[jsonComponentModule]
HttpMessageConvertersAutoConfiguration:
  - HttpMessageConverters[messageConverters]
  - StringHttpMessageConverterConfiguration:
      - StringHttpMessageConverter[stringHttpMessageConverter]

首先在这里EnableWebMvcConfiguration引入了好几个HandlerMapping,HandlerAdapter。那么什么是HandlerMapping?又什么是HandlerAdapter?

  • HandlerMapping与HandlerAdapter

在以前的Spring当中,用户在Web引用中必须编写HandlerMapping,主要是用于将前台的请求映射到对应的方法。而现在没那么麻烦了,因为Spring MVC提供了一系列的HandlerMapping实现,其中最重要的可能莫过于RequestMappingHandlerMapping了。它会去查询所有带有@Controller的Bean上面的@RequestMapping注解。然后进行匹配。比如当前台发起一个请求时,在SpringMVC当中就会遍历所有的HandlerMapping实现,然后获取处理器链,其实也就是说根据支持的HandlerMapping,构造处理器链。

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	if (this.handlerMappings != null) {
		for (HandlerMapping mapping : this.handlerMappings) {
			HandlerExecutionChain handler = mapping.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
	}
	return null;
}

在这里插入图片描述
比如第一个SimpleUrlHandlerMapping,首先需要获取到当前请求的路径。
在handlerMap当中保存了请求路径与Handler的映射。因为当前请求路径为/hello,在handlerMap当中不匹配,所以这个HandlerMapping不支持当前的请求。注意这里分为直接匹配和模板匹配。(Direct match和Pattern match)
在这里插入图片描述
直接匹配比较简单,就是简单的字符串匹配。而模板匹配则要使用到PathMatcher,通常的实现为AntPathMatcher,也就是Ant语法。相关规则只需要查看对应类的注解就可以了。
在这里插入图片描述
由于SimpleUrlHandlerMapping继承了AbstractUrlHandlerMapping,这里还包含了根路径处理和默认处理器相关逻辑。
在这里插入图片描述
对于WebMvcEndpointHandlerMapping,是用于actuator监控的。处理请求首先仍然是获取路径,在Spring当中是通过UrlPathHelper来实现的。

private UrlPathHelper urlPathHelper = new UrlPathHelper();
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);

然后接下来还要上锁,防止并发

/**
 * Look up a handler method for the given request.
 */
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
	String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
	this.mappingRegistry.acquireReadLock();
	try {
		HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
		return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
	}
	finally {
		this.mappingRegistry.releaseReadLock();
	}
}

最后一样还是通过映射关系查找处理器。
在这里插入图片描述
对于RequestMappingHandlerMapping,这个类也是继承了AbstractHandlerMethodMapping,获取请求路径以及加锁与上面的WebMvcEndpointHandlerMapping是一致的。
在这里插入图片描述
HandlerMapping主要就是用于将请求最后映射到对应的处理器方法(也就是用户写的方法)。比如定义如下

@RestController
@RequestMapping("/hello")
public class HelloWorldController {
    @GetMapping
    public ResponseEntity<String> hello() {
        return ResponseEntity.ok("Hello SpringFox!");
    }
}    

很明显,这里的请求/hello最终是需要请求到org.spring.swagger.security.controller.HelloWorldController#hello方法的。只不过在Spring当中进行了一系列的包装,最终HttpServletRequest -> HandlerExecutionChain(包装HandlerMethod) -> 对应Method。
在这里插入图片描述
在这里插入图片描述
正因为在这里只是包装了最终方法的对象,而需要到最终方法,还需要另一个工具HandlerAdapter。为啥要这个适配器呢?因为用户定义的控制层的各种方法千变万化,就拿返回参数来说,有可能是JSON,也有可能是View,也有可能是是ModelView,而这里统一当做返回ModelAndView处理。有了HandlerAdapter,外面的请求处理其实就变得简单了。流程可以简化到以下的模式
在这里插入图片描述

  • ServletInvocableHandlerMethod和HandlerMethodReturnValueHandler

获取到了HandlerAdapter之后,内部的逻辑就取决于具体的实现了,对于RequestMappingHandlerAdapter而言,会继续针对HandlerMethod进行封装成ServletInvocableHandlerMethod,然后调用的时候其实就是进行方法的反射调用。

public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {

	Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
	if (logger.isTraceEnabled()) {
		logger.trace("Arguments: " + Arrays.toString(args));
	}
	return doInvoke(args);
}

由于反射返回的结果(方法调用结果)也是各不相同,所以也需要针对不同的结果进行不同的处理,最后转为ModelAndView。这里负责转换结果就是HandlerMethodReturnValueHandler。对应的接口定义如下

/**
 * Strategy interface to handle the value returned from the invocation of a
 * handler method .
 *
 * @author Arjen Poutsma
 * @since 3.1
 * @see HandlerMethodArgumentResolver
 */
public interface HandlerMethodReturnValueHandler {

	/**
	 * Whether the given {@linkplain MethodParameter method return type} is
	 * supported by this handler.
	 * @param returnType the method return type to check
	 * @return {@code true} if this handler supports the supplied return type;
	 * {@code false} otherwise
	 */
	boolean supportsReturnType(MethodParameter returnType);

	/**
	 * Handle the given return value by adding attributes to the model and
	 * setting a view or setting the
	 * {@link ModelAndViewContainer#setRequestHandled} flag to {@code true}
	 * to indicate the response has been handled directly.
	 * @param returnValue the value returned from the handler method
	 * @param returnType the type of the return value. This type must have
	 * previously been passed to {@link #supportsReturnType} which must
	 * have returned {@code true}.
	 * @param mavContainer the ModelAndViewContainer for the current request
	 * @param webRequest the current request
	 * @throws Exception if the return value handling results in an error
	 */
	void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;

}

从结果的定义不难看出,这是一个典型的策略接口,一个方法用于匹配不同的返回结果类型,而另一个方法用于处理对应的结果。这里不妨看一个在SpringBoot中比较常用的实现RequestResponseBodyMethodProcessor。这个Processor支持什么样的返回类型呢?

@Override
public boolean supportsReturnType(MethodParameter returnType) {
	return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
			returnType.hasMethodAnnotation(ResponseBody.class));
}

从对应的实现不难看出,只要对应的方法或类上面有ResponseBody注解就可以了。在SpringBoot项目当中用@RestController注解,而这个注解当中是包含有ResponseBody的元注解,所以HelloWorldController示例中的hello方法也符合这种类型。在Spring当中,通过代理方式来进行结果处理,HandlerMethodReturnValueHandlerComposite这个类也实现了HandlerMethodReturnValueHandler接口,但是它并不会具体去处理返回结果,而是由内部的returnValueHandlers列表来处理,通过遍历这个链表,使用支持当前返回结果的HandlerMethodReturnValueHandler代理处理。

/**
 * Whether the given {@linkplain MethodParameter method return type} is supported by any registered
 * {@link HandlerMethodReturnValueHandler}.
 */
@Override
public boolean supportsReturnType(MethodParameter returnType) {
	return getReturnValueHandler(returnType) != null;
}

@Nullable
private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) {
	for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
		if (handler.supportsReturnType(returnType)) {
			return handler;
		}
	}
	return null;
}

supportsReturnType实现如上,只要内部的HandlerMethodReturnValueHandler列表支持,当前代理就支持。
进行返回结果处理

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

	HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
	if (handler == null) {
		throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
	}
	handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}

@Nullable
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
	boolean isAsyncValue = isAsyncReturnValue(value, returnType);
	for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
		if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
			continue;
		}
		if (handler.supportsReturnType(returnType)) {
			return handler;
		}
	}
	return null;
}

在这里插入图片描述
遍历以上结果,最后会使用HttpEntityMethodProcessor来处理,这不是和上面说的RequestResponseBodyMethodProcessor有冲突吗?其实在这里,这两个MethodProcessor其实都是支持的,HttpEntityMethodProcessor支持返回类型为HttpEntity,上面方法的返回结果恰好就是HttpEntity类型。

@Override
public boolean supportsParameter(MethodParameter parameter) {
	return (HttpEntity.class == parameter.getParameterType() ||
			RequestEntity.class == parameter.getParameterType());
}

不过由于先后顺序问题,第一个满足的进行处理。这个列表是在RequestMappingHandlerAdapter#getDefaultReturnValueHandlers中定义的。
在这里插入图片描述
如上图所示,在这里注意到构造这两个方法处理器时都传入了一个HttpMessageConverter的列表,而这个列表有啥用呢?看一下对应的接口定义

/**
 * Strategy interface that specifies a converter that can convert from and to HTTP requests and responses.
 *
 * @author Arjen Poutsma
 * @author Juergen Hoeller
 * @since 3.0
 * @param <T> the converted object type
 */
public interface HttpMessageConverter<T> {

	/**
	 * Indicates whether the given class can be read by this converter.
	 * @param clazz the class to test for readability
	 * @param mediaType the media type to read (can be {@code null} if not specified);
	 * typically the value of a {@code Content-Type} header.
	 * @return {@code true} if readable; {@code false} otherwise
	 */
	boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);

	/**
	 * Indicates whether the given class can be written by this converter.
	 * @param clazz the class to test for writability
	 * @param mediaType the media type to write (can be {@code null} if not specified);
	 * typically the value of an {@code Accept} header.
	 * @return {@code true} if writable; {@code false} otherwise
	 */
	boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);

	/**
	 * Return the list of {@link MediaType} objects supported by this converter.
	 * @return the list of supported media types
	 */
	List<MediaType> getSupportedMediaTypes();

	/**
	 * Read an object of the given type from the given input message, and returns it.
	 * @param clazz the type of object to return. This type must have previously been passed to the
	 * {@link #canRead canRead} method of this interface, which must have returned {@code true}.
	 * @param inputMessage the HTTP input message to read from
	 * @return the converted object
	 * @throws IOException in case of I/O errors
	 * @throws HttpMessageNotReadableException in case of conversion errors
	 */
	T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
			throws IOException, HttpMessageNotReadableException;

	/**
	 * Write an given object to the given output message.
	 * @param t the object to write to the output message. The type of this object must have previously been
	 * passed to the {@link #canWrite canWrite} method of this interface, which must have returned {@code true}.
	 * @param contentType the content type to use when writing. May be {@code null} to indicate that the
	 * default content type of the converter must be used. If not {@code null}, this media type must have
	 * previously been passed to the {@link #canWrite canWrite} method of this interface, which must have
	 * returned {@code true}.
	 * @param outputMessage the message to write to
	 * @throws IOException in case of I/O errors
	 * @throws HttpMessageNotWritableException in case of conversion errors
	 */
	void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
			throws IOException, HttpMessageNotWritableException;

}

这里仍然是一个策略接口,通过canRead、canWrite判断是否支持当前类和媒体类型,而read和write进行相应的反序列化和序列化操作,在SpringBoot中我们方法返回一个对象,而到了前台是转为JSON格式的,Swagger更是如此,这个序列化工作其实就是靠HandlerMethodReturnValueHandler来处理的,而HandlerMethodReturnValueHandler又是靠HttpMessageConverter来实现的。因为不同的MediaType和返回类型继续搭配匹配不同的HttpMessageConverter。

  • HttpMessageConverter

通过HandlerMethodReturnValueHandler来进行返回结果处理还是不够,因为还要考虑不同的MediaType。比如说,有时候需要返回JSON,而有时候又需要返回XML。
在这里插入图片描述
比如所有实现AbstractXmlHttpMessageConverter抽象类的消息转换器支持的媒体类型如下所示(比如application/xml

protected AbstractXmlHttpMessageConverter() {
	super(MediaType.APPLICATION_XML, MediaType.TEXT_XML, new MediaType("application", "*+xml"));
}

而MappingJackson2HttpMessageConverter如下所示(比如application/json

public MappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
	super(objectMapper, MediaType.APPLICATION_JSON, new MediaType("application", "*+json"));
}

通过方法返回类型匹配HandlerMethodReturnValueHandler,然后通过MediaType匹配HttpMessageConverter,最后完成对应的序列化和反序列化。一个HandlerMethodReturnValueHandler支持的所有MediaType就是内部HttpMessageConverter支持的MediaType的超集。如下图所示,HttpEntityMethodProcessor一共支持10种MediaType。
在这里插入图片描述
RequestMappingHandlerAdapter#getDefaultReturnValueHandlers这个方法为private访问类型,不难得出,在Spring MVC当中,HandlerMethodReturnValueHandler是没法用户配置的。而HttpMessageConverter则不同。在WebMvcConfigurationSupport#getMessageConverters方法中

protected final List<HttpMessageConverter<?>> getMessageConverters() {
	if (this.messageConverters == null) {
		this.messageConverters = new ArrayList<>();
		configureMessageConverters(this.messageConverters);
		if (this.messageConverters.isEmpty()) {
			addDefaultHttpMessageConverters(this.messageConverters);
		}
		extendMessageConverters(this.messageConverters);
	}
	return this.messageConverters;
}

通过addDefaultHttpMessageConverters配置默认的,而extendMessageConverters进行扩展。

RequestMappingHandlerAdapter类型Bean是在WebMvcAutoConfiguration.EnableWebMvcConfiguration中定义的,而EnableWebMvcConfiguration继承自DelegatingWebMvcConfiguration,然后又继承自WebMvcConfigurationSupport,所以adapter当中的messageConverters取决于上面方法

extendMessageConverters是在DelegatingWebMvcConfiguration当中定义的,可以看出这里注入了所有的WebMvcConfigurer,然后提取其中的HttpMessageConverter加入到列表当中。

private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();


@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
	if (!CollectionUtils.isEmpty(configurers)) {
		this.configurers.addWebMvcConfigurers(configurers);
	}
}

@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
	this.configurers.extendMessageConverters(converters);
}

WebMvcConfigurerComposite作为一个WebMvcConfigurer代理,管理了一个WebMvcConfigurer类型列表,所有操作都是代理到列表中的WebMvcConfigurer当中。跟上面的HandlerMethodReturnValueHandlerComposite一样,这里不继续深入介绍了。

对于扩展HttpMessageConverter,官方文档当中介绍了两种配置的方式,第一种直接定义
在这里插入图片描述
对于这种方式,是怎么起作用的呢?WebMvcAutoConfigurationAdapter继承自WebMvcConfigurer,在实例化的时候会注入ObjectProvider<HttpMessageConverters>,在设置自定义的HttpMessageConverter的时候(extendMessageConverters方法)会调用这个实例configureMessageConverters方法,然后通过ObjectProvider的ifAvailable方法,这个方法定义如下

default void ifAvailable(Consumer<T> dependencyConsumer) throws BeansException {
	T dependency = getIfAvailable();
	if (dependency != null) {
		dependencyConsumer.accept(dependency);
	}
}
--DefaultListableBeanFactory类中定义的
@Override
@Nullable
public Object getIfAvailable() throws BeansException {
	if (this.optional) {
		return createOptionalDependency(this.descriptor, this.beanName);
	}
	else {
		DependencyDescriptor descriptorToUse = new DependencyDescriptor(this.descriptor) {
			@Override
			public boolean isRequired() {
				return false;
			}
		};
		return doResolveDependency(descriptorToUse, this.beanName, null, null);
	}
}

以上的getIfAvailable会调用DefaultListableBeanFactory中的doResolveDependency方法。
在这里插入图片描述
在这里会首先查找HttpMessageConverters类型的Bean,然后获取其中的converters列表并加入到WebMvcConfigurationSupport#messageConverters当中。以下为系统默认定义的
在这里插入图片描述
但是HttpMessageConverters这个类并没有这么单纯。

public HttpMessageConverters(Collection<HttpMessageConverter<?>> additionalConverters) {
	this(true, additionalConverters);
}

这个构造器传入的只是额外的HttpMessageConverter,在构造内部还会加入默认的(addDefaultConverters为true).

public HttpMessageConverters(boolean addDefaultConverters, Collection<HttpMessageConverter<?>> converters) {
	List<HttpMessageConverter<?>> combined = getCombinedConverters(converters,
			addDefaultConverters ? getDefaultConverters() : Collections.emptyList());
	combined = postProcessConverters(combined);
	this.converters = Collections.unmodifiableList(combined);
}

获取默认的又会回到WebMvcConfigurationSupport的逻辑。也就是WebMvcConfigurationSupport#addDefaultHttpMessageConverters当中。

private List<HttpMessageConverter<?>> getDefaultConverters() {
	List<HttpMessageConverter<?>> converters = new ArrayList<>();
	if (ClassUtils.isPresent("org.springframework.web.servlet.config.annotation." + "WebMvcConfigurationSupport",
			null)) {
		converters.addAll(new WebMvcConfigurationSupport() {

			public List<HttpMessageConverter<?>> defaultMessageConverters() {
				return super.getMessageConverters();
			}

		}.defaultMessageConverters());
	}
	else {
		converters.addAll(new RestTemplate().getMessageConverters());
	}
	reorderXmlConvertersToEnd(converters);
	return converters;
}

在这里插入图片描述
因为上面定义的WebMvcConfigurationSupport的没有扩展configureMessageConverters方法,所以通过红色标注的代码之后,messageConverters一定为空,所以一定会添加所有默认的HttpMessageConverter的实现列表。接下来extendMessageConverters仍然没有覆盖,也是空逻辑。所以最后创建的HttpMessageConverters实例就包含了默认的列表和构造参数传入的列表。如下所示
在这里插入图片描述
所以通过注入HttpMessageConverters类型Bean来提供HttpMessageConverter列表的方式是在WebMvcAutoConfigurationAdapter这个WebMvcConfigurer当中实现的。通过以上分析,我们也不难看出,通过WebMvcConfigurer的configureMessageConverters方法也完全是一样的。

通过WebMvcConfigurer的extendMessageConverters也是可以添加的。

第二种是间接修改原有的
在这里插入图片描述

All @JsonComponent beans in the ApplicationContext are automatically registered with Jackson. Because @JsonComponent is meta-annotated with @Component, the usual component-scanning rules apply.

Spring Boot also provides JsonObjectSerializer and JsonObjectDeserializer base classes that provide useful alternatives to the standard Jackson versions when serializing objects. See JsonObjectSerializer and JsonObjectDeserializer in the Javadoc for details.

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-08-08 11:07:41  更:2021-08-08 11:12:25 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/10 18:12:00-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码