最近我们的项目有一个需求,需要对后端返回给前端的JSON格式进行规范化处理。要求不能缺少任何字段,并且字段的值不能为null。为了实现这个需求,我开始思考如何在Spring Boot中自定义Jackson序列化器。首先,我会先尝试实现这个功能,然后再深入研究源代码。
实际应用
第一步:编写相关配置类
代码语言:java复制@Configuration
public class WebConfiguration extends WebMvcConfigurationSupport {
@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.stream().filter(c -> c instanceof MappingJackson2HttpMessageConverter)
.map(c ->(MappingJackson2HttpMessageConverter)c)
.forEach(c->{
ObjectMapper mapper = c.getObjectMapper();
// 为mapper注册一个带有SerializerModifier的Factory,此modifier主要做的事情为:当序列化类型为array,list、set时,当值为空时,序列化成[]
mapper.setSerializerFactory(mapper.getSerializerFactory().withSerializerModifier(new MyBeanSerializerModifier()));
c.setObjectMapper(mapper);
});
}
}
第二步:当值为null时,我们可以编写自定义的序列化方法,以确保在序列化过程中不会出现任何错误。以下是一个示例:
代码语言:java复制public class MyBeanSerializerModifier extends BeanSerializerModifier {
private MyNullStringJsonSerializer myNullStringJsonSerializer;
private MyNullArrayJsonSerializer MyNullArrayJsonSerializer;
private MyNullObjectJsonSerializer MyNullObjectJsonSerializer;
private MyNullJsonSerializer myNullJsonSerializer;
public MyBeanSerializerModifier(){
myNullStringJsonSerializer = new MyNullStringJsonSerializer();
MyNullArrayJsonSerializer = new MyNullArrayJsonSerializer();
MyNullObjectJsonSerializer = new MyNullObjectJsonSerializer();
myNullJsonSerializer = new MyNullJsonSerializer();
}
@Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc,
List<BeanPropertyWriter> beanProperties) {
// 循环所有的beanPropertyWriter
beanProperties.forEach(writer ->{
// 判断字段的类型
if (isArrayType(writer)) {
//给writer注册一个自己的nullSerializer
writer.assignNullSerializer(MyNullArrayJsonSerializer);
} else if (isObjectType(writer)) {
writer.assignNullSerializer(MyNullObjectJsonSerializer);
} else if (isStringType(writer)) {
writer.assignNullSerializer(myNullStringJsonSerializer);
} else if (isPrimitiveType(writer)) {
writer.assignNullSerializer(myNullJsonSerializer);
}
});
return beanProperties;
}
// 判断是否是boolean类型
private boolean isPrimitiveType(BeanPropertyWriter writer) {
Class<?> clazz = writer.getType().getRawClass();
return clazz.isPrimitive();
}
// 判断是否是string类型
private boolean isStringType(BeanPropertyWriter writer) {
Class<?> clazz = writer.getType().getRawClass();
return clazz.equals(String.class);
}
// 判断是否是对象类型
private boolean isObjectType(BeanPropertyWriter writer) {
Class<?> clazz = writer.getType().getRawClass();
return !clazz.isPrimitive() && !clazz.equals(String.class)
&& clazz.isAssignableFrom(Object.class);
}
// 判断是否是集合类型
protected boolean isArrayType(BeanPropertyWriter writer) {
Class<?> clazz = writer.getType().getRawClass();
return clazz.isArray() || clazz.equals(List.class) || clazz.equals(Set.class);
}
class MyNullJsonSerializer extends JsonSerializer<Object>{
@Override
public void serialize(Object value, JsonGenerator jgen, SerializerProvider serializers) throws IOException {
if (value == null) {
jgen.writeNull();
}
}
}
class MyNullStringJsonSerializer extends JsonSerializer<Object>{
@Override
public void serialize(Object value, JsonGenerator jgen, SerializerProvider serializers) throws IOException {
if (value == null) {
jgen.writeString(StringUtils.EMPTY);
}
}
}
class MyNullArrayJsonSerializer extends JsonSerializer<Object>{
@Override
public void serialize(Object value, JsonGenerator jgen, SerializerProvider serializers) throws IOException {
if (value == null) {
jgen.writeStartArray();
jgen.writeEndArray();
}
}
}
class MyNullObjectJsonSerializer extends JsonSerializer<Object>{
@Override
public void serialize(Object value, JsonGenerator jgen, SerializerProvider serializers) throws IOException {
if (value == null) {
jgen.writeStartObject();
jgen.writeEndObject();
}
}
}
}
这样,我们只需完成基本配置,接下来可以尝试效果了。首先,我们可以定义一个bean来处理返回结果,并创建一个简单的controller来接收请求。博主不再详细编写这两个类。下面是返回的结果。
这是我项目中需要实现的需求,大家可以根据自己的需求来修改MyBeanSerializerModifier这个类。另外,还有另一种实现方式,可以不需要继承。
代码语言:java复制@Configuration
public class WebConfiguration {
@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(){
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper mapper = mappingJackson2HttpMessageConverter.getObjectMapper();
mapper.setSerializerFactory(mapper.getSerializerFactory().withSerializerModifier(new MyBeanSerializerModifier()));
mappingJackson2HttpMessageConverter.setObjectMapper(mapper);
return mappingJackson2HttpMessageConverter;
}
}
这种方法也是可以成功设置的。主要是因为继承了WebMvcConfigurationSupport类,这个类有很多可以自定义的方法,使用起来很方便。
首先,我们来解决第一个问题:为什么继承WebMvcConfigurationSupport后,需要重写extendMessageConverters方法。这是因为在WebMvcConfigurationSupport类中,extendMessageConverters方法被调用来扩展默认的消息转换器列表。我们需要重写这个方法,以便添加我们自己的消息转换器。
接下来,我们解决第二个问题:为什么继承WebMvcConfigurationSupport后,生成的@Bean的MappingJackson2HttpMessageConverter不起作用。
最后,我们来解决第三个问题:为什么不继承WebMvcConfigurationSupport时,生成的@Bean的MappingJackson2HttpMessageConverter起作用。
综上所述,我们需要进入源码观察,在哪里进行了Jackson序列化。首先,我们要清楚在哪里进行了Jackson序列化。看这里:
从返回请求开始的序列化基本流程就在这里了。在这个流程中,我们可以看到每一步清晰记录的源码,尽管图示可能有些简单,但它提供了对整个流程的清晰理解。
源码分析
接下来,我们将重点关注下面提供的源码。
代码语言:java复制 //序列化每一个字段
protected void serializeFields(Object bean, JsonGenerator gen, SerializerProvider provider)
throws IOException
{
final BeanPropertyWriter[] props;
if (_filteredProps != null && provider.getActiveView() != null) {
props = _filteredProps;
} else {
props = _props;
}
int i = 0;
try {
for (final int len = props.length; i < len; i) {
BeanPropertyWriter prop = props[i];
if (prop != null) { // can have nulls in filtered list
//关键就在这一步进行的序列化,而为什么BeanPropertyWriter是数组,我们一会解释
prop.serializeAsField(bean, gen, provider);
}
}
if (_anyGetterWriter != null) {
_anyGetterWriter.getAndSerialize(bean, gen, provider);
}
......
}
}
具体实现
之前的流程不是本文的重点,所以我就不再重复粘贴了。既然我们已经确定了需要进行序列化的位置,那么现在让我们来看一下具体的实现方法:
代码语言:java复制 @Override
public void serializeAsField(Object bean, JsonGenerator gen,
SerializerProvider prov) throws Exception {
// inlined 'get()'
final Object value = (_accessorMethod == null) ? _field.get(bean)
: _accessorMethod.invoke(bean, (Object[]) null);
// Null handling is bit different, check that first
if (value == null) {
//看到这里大家应该就知道null值是如何进行序列化 的了,如果不配置的话,默认是返回null
//因为_nullSerializer是有默认值的,大家看一看这个类的初始化
//那我们要是改一下_nullSerializer的这个默认类,让每一个字段调用我们自己的_nullSerializer不就可以了吗?
if (_nullSerializer != null) {
gen.writeFieldName(_name);
_nullSerializer.serialize(null, gen, prov);
}
return;
}
// then find serializer to use
JsonSerializer<Object> ser = _serializer;
if (ser == null) {
Class<?> cls = value.getClass();
PropertySerializerMap m = _dynamicSerializers;
ser = m.serializerFor(cls);
if (ser == null) {
//在这里会找JsonSerialize、JsonRawValue注解类
ser = _findAndAddDynamic(m, cls, prov);
}
}
// and then see if we must suppress certain values (default, empty)
if (_suppressableValue != null) {
if (MARKER_FOR_EMPTY == _suppressableValue) {
if (ser.isEmpty(prov, value)) {
return;
}
} else if (_suppressableValue.equals(value)) {
return;
}
}
// For non-nulls: simple check for direct cycles
if (value == bean) {
// three choices: exception; handled by call; or pass-through
if (_handleSelfReference(bean, gen, prov, ser)) {
return;
}
}
gen.writeFieldName(_name);
if (_typeSerializer == null) {
//使用你的JsonSerialize序列化一下
ser.serialize(value, gen, prov);
} else {
ser.serializeWithType(value, gen, prov, _typeSerializer);
}
}
问题一:为什么继承WebMvcConfigurationSupport后,要重写extendMessageConverters方法?
那我们来解决第一个问题:为什么继承WebMvcConfigurationSupport后,要重写extendMessageConverters方法?
在我们的请求进入时,如果我们的配置类继承了WebMvcConfigurationSupport类,那么它会重写extendMessageConverters方法。我们知道dispatchservlet会处理handle请求,而dispatchservlet实际上是由RequestMappingHandlerAdapter类来处理的。这个类是在WebMvcConfigurationSupport中进行配置的,我们可以通过查看源码来验证这一点:
代码语言:java复制@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcValidator") Validator validator) {
RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
adapter.setContentNegotiationManager(contentNegotiationManager);
adapter.setMessageConverters(getMessageConverters());
adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer(conversionService, validator));
adapter.setCustomArgumentResolvers(getArgumentResolvers());
adapter.setCustomReturnValueHandlers(getReturnValueHandlers());
adapter.setCallableInterceptors(configurer.getCallableInterceptors());
....
return adapter;
}
adapter.setMessageConverters(getMessageConverters());当大家看到这个方法的时候,应该就会想到我们的默认Jackson转换器:MappingJackson2HttpMessageConverter。让我们来看看这个getMessageConverters()方法有什么特别的地方:
代码语言:java复制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;
}
代码语言:java复制 protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
//这些都不用管,跟我们的需求没啥作用,我们只看关键的部分,在下面
messageConverters.add(new ByteArrayHttpMessageConverter());
messageConverters.add(new StringHttpMessageConverter());
messageConverters.add(new ResourceHttpMessageConverter());
messageConverters.add(new ResourceRegionHttpMessageConverter());
try {
messageConverters.add(new SourceHttpMessageConverter<>());
}
catch (Throwable ex) {
// Ignore when no TransformerFactory implementation is available...
}
messageConverters.add(new AllEncompassingFormHttpMessageConverter());
if (romePresent) {
messageConverters.add(new AtomFeedHttpMessageConverter());
messageConverters.add(new RssChannelHttpMessageConverter());
}
if (jackson2XmlPresent) {
Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.xml();
if (this.applicationContext != null) {
builder.applicationContext(this.applicationContext);
}
messageConverters.add(new MappingJackson2XmlHttpMessageConverter(builder.build()));
}
else if (jaxb2Present) {
messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
}
if (jackson2Present) { Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json();
if (this.applicationContext != null) {
builder.applicationContext(this.applicationContext);
}
//解析我们返回值的转换器就是在这里生成的
messageConverters.add(new MappingJackson2HttpMessageConverter(builder.build()));
}
else if (gsonPresent) {
messageConverters.add(new GsonHttpMessageConverter());
}
else if (jsonbPresent) {
messageConverters.add(new JsonbHttpMessageConverter());
}
if (jackson2SmilePresent) {
Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.smile();
if (this.applicationContext != null) {
builder.applicationContext(this.applicationContext);
}
messageConverters.add(new MappingJackson2SmileHttpMessageConverter(builder.build()));
}
if (jackson2CborPresent) {
Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.cbor();
if (this.applicationContext != null) {
builder.applicationContext(this.applicationContext);
}
messageConverters.add(new MappingJackson2CborHttpMessageConverter(builder.build()));
}
}
我们的MappingJackson2HttpMessageConverter类就是在这里进行初始化的。在初始化的过程中,默认的_nullSerializer也会被初始化。可能大家会说,这已经完成了初始化,那接下来该怎么办呢?大家应该注意到了extendMessageConverters(this.messageConverters)这个方法。这个方法是用来重写实现的。现在我们知道为什么在继承WebMvcConfigurationSupport后要重写extendMessageConverters了。在我们的配置类中,我们遍历已经获取到的converters,并对我们想要的转换器进行修改和添加。那么修改完成后,它在哪里起作用呢?现在让我们来看一下源码:
在进行序列化之前,可以在调用writeWithMessageConverters方法之前执行一些修改操作的方法。
代码语言:java复制 protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
.......
MediaType selectedMediaType = null;
MediaType contentType = outputMessage.getHeaders().getContentType();
boolean isContentTypePreset = contentType != null && contentType.isConcrete();
if (isContentTypePreset) {
if (logger.isDebugEnabled()) {
logger.debug("Found 'Content-Type:" contentType "' in response");
}
selectedMediaType = contentType;
}
else {
HttpServletRequest request = inputMessage.getServletRequest();
List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);
List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
.......
if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();
//这里进行选择我们的MappingJackson2HttpMessageConverter去自定义序列化
for (HttpMessageConverter<?> converter : this.messageConverters) {
GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
(GenericHttpMessageConverter<?>) converter : null);
if (genericConverter != null ?
((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
converter.canWrite(valueType, selectedMediaType)) {
body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
inputMessage, outputMessage);
if (body != null) {
Object theBody = body;
LogFormatUtils.traceDebug(logger, traceOn ->
"Writing [" LogFormatUtils.formatValue(theBody, !traceOn) "]");
addContentDispositionHeader(inputMessage, outputMessage);
if (genericConverter != null) {
//开始序列化
genericConverter.write(body, targetType, selectedMediaType, outputMessage);
}
else {
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Nothing to write: null body");
}
}
return;
}
}
}
if (body != null) {
Set<MediaType> producibleMediaTypes =
(Set<MediaType>) inputMessage.getServletRequest()
.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
if (isContentTypePreset || !CollectionUtils.isEmpty(producibleMediaTypes)) {
throw new HttpMessageNotWritableException(
"No converter for [" valueType "] with preset Content-Type '" contentType "'");
}
throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
}
}
我们一直追踪getProducibleMediaTypes后,最终发现会调用BeanSerializerFactory的constructBeanOrAddOnSerializer,就是这里进行修改操作的。
代码语言:java复制protected JsonSerializer<Object> constructBeanSerializer(SerializerProvider prov,
BeanDescription beanDesc)
throws JsonMappingException
{
// 13-Oct-2010, tatu: quick sanity check: never try to create bean serializer for plain Object
// 05-Jul-2012, tatu: ... but we should be able to just return "unknown type" serializer, right?
if (beanDesc.getBeanClass() == Object.class) {
return prov.getUnknownTypeSerializer(Object.class);
// throw new IllegalArgumentException("Cannot create bean serializer for Object.class");
}
final SerializationConfig config = prov.getConfig();
BeanSerializerBuilder builder = constructBeanSerializerBuilder(beanDesc);
builder.setConfig(config);
// First: any detectable (auto-detect, annotations) properties to serialize?
List<BeanPropertyWriter> props = findBeanProperties(prov, beanDesc, builder);
if (props == null) {
props = new ArrayList<BeanPropertyWriter>();
} else {
props = removeOverlappingTypeIds(prov, beanDesc, builder, props);
}
// [databind#638]: Allow injection of "virtual" properties:
prov.getAnnotationIntrospector().findAndAddVirtualProperties(config, beanDesc.getClassInfo(), props);
// [JACKSON-440] Need to allow modification bean properties to serialize:
if (_factoryConfig.hasSerializerModifiers()) {
for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
props = mod.changeProperties(config, beanDesc, props);
}
}
.......//此处省略
}
大家请注意一下props = mod.changeProperties(config, beanDesc, props);
我们在配置类中为MappingJackson2HttpMessageConverter配置了withSerializerModifier方法,并且设置了自定义的MyBeanSerializerModifier类来继承BeanSerializerModifier并重写changeProperties方法。因此,在调用changeProperties方法时,我们可以对null值的序列化进行修改。同时,我们还返回了一个list类型的BeanPropertyWriter,这就解释了为什么在解析时,该BeanPropertyWriter是一个数组类型,因为不同字段的解析方式是不同的。
另外,让我们解释一下为什么单独配置并设置实例化@bean的MappingJackson2HttpMessageConverter也是有效的。大家可以查看一下JacksonHttpMessageConvertersConfiguration类的源代码,其中明确使用了@ConditionalOnMissingBean注解。这意味着,如果我们自己进行了配置并且实例化了该bean,那么这个备胎就会被忽略,以我们自己的配置为准。这个问题就不再多说了。
问题二:为什么继承WebMvcConfigurationSupport后,生成的@Bean的MappingJackson2HttpMessageConverter不起作用。
让我们进一步分析第二个问题:当继承WebMvcConfigurationSupport后,为什么生成的@Bean的MappingJackson2HttpMessageConverter不起作用?这个问题需要与第三个问题一起解决:为什么当不继承WebMvcConfigurationSupport时,生成的@Bean的MappingJackson2HttpMessageConverter是起作用的。
我们知道,当我们继承WebMvcConfigurationSupport类后,会导致自动注入的mvc配置失效。这是因为@SpringBootApplication注解中包含@EnableAutoConfiguration注解,该注解会引入AutoConfigurationImportSelector类。该类会扫描spring.factories文件,该文件位于org.springframework.boot:spring-boot-autoconfigure下。在该文件中,有一个默认的mvn配置,也是继承了WebMvcConfigurationSupport的,名称为WebMvcAutoConfiguration。现在我们来看一下WebMvcAutoConfiguration的源码:
代码语言:java复制@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
//注意此处有一个ConditionalOnMissingBean注解,所以如果我们自己继承后,就相当于已经存在WebMvcConfigurationSupport类,
//就会走我们自己的配置类,此配置会失效
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
.....
@Configuration(proxyBeanMethods = false)
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
private final ResourceProperties resourceProperties;
private final WebMvcProperties mvcProperties;
private final ListableBeanFactory beanFactory;
private final WebMvcRegistrations mvcRegistrations;
private ResourceLoader resourceLoader;
public EnableWebMvcConfiguration(ResourceProperties resourceProperties,
ObjectProvider<WebMvcProperties> mvcPropertiesProvider,
ObjectProvider<WebMvcRegistrations> mvcRegistrationsProvider, ListableBeanFactory beanFactory) {
this.resourceProperties = resourceProperties;
this.mvcProperties = mvcPropertiesProvider.getIfAvailable();
this.mvcRegistrations = mvcRegistrationsProvider.getIfUnique();
this.beanFactory = beanFactory;
}
//如果我们不继承的话,处理请求的RequestMappingHandlerAdapter就会在这里生成
//会调用DelegatingWebMvcConfiguration里面的 requestMappingHandlerAdapter方法,
@Bean
@Override
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcValidator") Validator validator) {
RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter(contentNegotiationManager,
conversionService, validator);
adapter.setIgnoreDefaultModelOnRedirect(
this.mvcProperties == null || this.mvcProperties.isIgnoreDefaultModelOnRedirect());
return adapter;
}
.....
}
不知道大家是否还记得,在调用getMessageConverters()方法时,之前会调用一个configureMessageConverters(this.messageConverters)方法来添加默认的messageConverters。然而,我们的DelegatingWebMvcConfiguration已经重写了这个方法,因此如果我们不继承WebMvcConfigurationSupport,我们使用@Bean注解形式定义的MappingJackson2HttpMessageConverter将不会被扫描到。
代码语言:java复制@Override
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
this.configurers.configureMessageConverters(converters);
}
//会添加我们的convert
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
for (WebMvcConfigurer delegate : this.delegates) {
delegate.configureMessageConverters(converters);
}
}
现在我们已经成功配置了自定义的Jackson序列化,然而,如果你仔细观察我的流程图,你会发现调用序列化的实际上是通过RequestResponseBodyMethodProcessor的handleReturnValue方法。
代码语言:java复制@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
mavContainer.setRequestHandled(true);
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
// Try even with null return value. ResponseBodyAdvice could get involved.
//这里进入序列化流程
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
最后,在序列化过程中,我们依然会从这个类或其父类中调用一个属性,即messageConverters。
代码语言:java复制public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver {
private static final Set<HttpMethod> SUPPORTED_METHODS =
EnumSet.of(HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH);
private static final Object NO_VALUE = new Object();
protected final Log logger = LogFactory.getLog(getClass());
//这个属性取值的
protected final List<HttpMessageConverter<?>> messageConverters;
protected final List<MediaType> allSupportedMediaTypes;
private final RequestResponseBodyAdviceChain advice;
...
}
于是,小伙伴们开始产生疑惑。我们自定义的属性在RequestMappingHandlerAdapter里面,与这个类似乎没有直接关系,那么这些属性是如何设置进来的呢?为了解答这个问题,我们不妨来研究一下RequestMappingHandlerAdapter的源码。仔细观察你会发现,RequestMappingHandlerAdapter类实现了InitializingBean接口。这意味着在创建RequestMappingHandlerAdapter的时候,会调用afterPropertiesSet方法。至于为什么会这样,我们可以通过查看源码来了解(虽然这不是主要的流程,但还是值得一看)。
代码语言:java复制 //在createBean的时候会调用这个方法,看看是否实现了InitializingBean
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking afterPropertiesSet() on bean with name '" beanName "'");
}
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
((InitializingBean) bean).afterPropertiesSet();
return null;
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
//在这里进行调用的,
((InitializingBean) bean).afterPropertiesSet();
}
}
if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
让我们来仔细看一下RequestMappingHandlerAdapter的afterPropertiesSet方法具体做了哪些操作吧。
代码语言:java复制@Override
public void afterPropertiesSet() {
// Do this first, it may add ResponseBody advice beans
initControllerAdviceCache();
if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.initBinderArgumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.returnValueHandlers == null) {
//是在这里生成的类
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
代码语言:java复制private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>();
// Single-purpose return value types
handlers.add(new ModelAndViewMethodReturnValueHandler());
handlers.add(new ModelMethodProcessor());
handlers.add(new ViewMethodReturnValueHandler());
handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(),
this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager));
handlers.add(new StreamingResponseBodyReturnValueHandler());
handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
this.contentNegotiationManager, this.requestResponseBodyAdvice));
handlers.add(new HttpHeadersReturnValueHandler());
handlers.add(new CallableMethodReturnValueHandler());
handlers.add(new DeferredResultMethodReturnValueHandler());
handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));
// Annotation-based return value types
handlers.add(new ModelAttributeMethodProcessor(false));
//看到这个类了吗?生成的时候将RequestMappingHandlerAdapter里面的转换器设置进去了
handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
this.contentNegotiationManager, this.requestResponseBodyAdvice));
// Multi-purpose return value types
handlers.add(new ViewNameMethodReturnValueHandler());
handlers.add(new MapMethodProcessor());
// Custom return value types
if (getCustomReturnValueHandlers() != null) {
handlers.addAll(getCustomReturnValueHandlers());
}
// Catch-all
if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
}
else {
handlers.add(new ModelAttributeMethodProcessor(true));
}
return handlers;
}
总结
在这里,我不确定大家是否理解了多少。这些问题都是我在遇到需求后自己提出并通过查看源码来回答的。我希望大家也能理解源码。源码是最好的老师,但是也不要太沉迷于此,毕竟源码的作用是提高我们的开发效率,而不是为了应对面试。所以要把握好学习源码的度,根据实际需求来学习和使用,提高自己的开发能力。
我正在参与2024腾讯技术创作特训营第五期有奖征文,快来和我瓜分大奖!