目录
引例:HTTPMessageConverters与Objectprovider
Springboot版本:2.5.1
引例:HTTPMessageConverters与Objectprovider
HTTPMessageConverter在RestController中扮演重要角色,具体来讲,配合相关注解,其能够将输入、输出的Java对象转化为Json序列串,当然,也能够依据不同的Converter将Java对象转换为其他消息形式例如XML。
不少教程曾有提及使用FastJson代替原有序列化工具类的配置方法,例如:
@Bean
FastJsonHttpMessageConverter fastJsonHttpMessageConverte(ObjectProvider<Book> books){
final FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setDateFormat("yyyy-MM-dd");
converter.setFastJsonConfig(fastJsonConfig);
return converter;
}
但是,这些教程均没有交代如此配置能够生效的原因。
笔者的研究思路是寻找网上关于HTTPMessageConverter的源码分析贴,结果发现FastJson配置类之所以能被正确加载,与“Objectprovider”相关:
方法org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration中,ObjectProvider作为Bean实例messageConverters的构造函数的参数被使用,且没有用到autowired注解:
@Bean
@ConditionalOnMissingBean
public HttpMessageConverters messageConverters(ObjectProvider<HttpMessageConverter<?>> converters) {
return new HttpMessageConverters((Collection)converters.orderedStream().collect(Collectors.toList()));
}
//相应的构造方法:
public HttpMessageConverters(Collection<HttpMessageConverter<?>> additionalConverters)
{
this(true, additionalConverters);
}
Spring官方文档 里对于@Autowired注解可省略的场合,是这么说的:
If a bean has more than one constructor, you’ll need to mark the one you want Spring to use with
@Autowired
而上述代码中Bean messageConverters只有一个构造方法,因此@Autowired并非必需。
但是,什么时候一个Bean需要多个构造方法,又如何在创建Bean时,调用对应地构造方法呢?这个问题有待另作讨论。
通过Debug,确认此处的ObjectProvider加载了包括FastJson在内的自定义messageConverters Bean。此外,这里我们发现ObjectProvider能够把满足条件的多个Bean实例一并提供出来。
试用Objectprovider
ObjectProvider与DependOn注解
在上文的Debug过程中我们同时发现ObjectProvider载入Bean的时点位于单例Bean messageConverters被getBean载入时,不难想到一个问题:如果ObjectProvider载入了一个未被初始化完成的Bean,岂不就非法访问了吗?
为了模拟这一情况,我对实验代码进行少许修改,修改了FastJson配置类的构造参数、使用了DependsOn注解。使得Bean实体book2的加载依赖于fastJson加载,也就是说,其会晚于fastJson类加载:
@DependsOn("converter")
@Bean
Book book2(){
Book ret = new Book();
ret.setId(222);
ret.setName("Book2");
ret.setAuthor("fff");
return ret;
}
@Bean(name="converter")
FastJsonHttpMessageConverter fastJsonHttpMessageConverte(ObjectProvider<Book> books){
final FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setDateFormat("yyyy-MM-dd");
converter.setFastJsonConfig(fastJsonConfig);
return converter;
}
使用相同的Debug方法,发现抛出了一个循环依赖异常,凭直觉来看,ObjectProvider应该是在这个时点进行了book2的实例化操作。在下一部分中我将展示相关源码以证明猜测的正确性。
源码分析
继承ObjectProvider接口的非抽象类只有DependencyObjectProvider一个,下面开始分析与上述性质相关的逻辑。
org.springframework.beans.factory.support.DefaultListableBeanFactory.DependencyObjectProvider#orderedStream
public Stream<Object> orderedStream() {
return this.resolveStream(true);
}
org.springframework.beans.factory.support.DefaultListableBeanFactory.DependencyObjectProvider#resolveStream
private Stream<Object> resolveStream(boolean ordered) {
DependencyDescriptor descriptorToUse = new DefaultListableBeanFactory.StreamDependencyDescriptor(this.descriptor, ordered);
// 获取指定类型的Bean
Object result = DefaultListableBeanFactory.this.doResolveDependency(descriptorToUse, this.beanName, (Set)null, (TypeConverter)null);
return result instanceof Stream ? (Stream)result : Stream.of(result);
}
org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency
主要关注这两组语句:
String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this, requiredType, true, descriptor.isEager());
String[] var12 = candidateNames;
int var14 = candidateNames.length;
for(int var16 = 0; var16 < var14; ++var16) {
String candidate = var12[var16];
if (!this.isSelfReference(beanName, candidate) && this.isAutowireCandidate(candidate, descriptor)) {
this.addCandidateEntry(result, candidate, descriptor, requiredType);
}
}
org.springframework.beans.factory.support.DefaultListableBeanFactory#addCandidateEntry
主要关注该函数的第3个if-else分支:
beanInstance = descriptor.resolveCandidate(candidateName, requiredType, this);
candidates.put(candidateName, beanInstance instanceof NullBean ? null : beanInstance);
org.springframework.beans.factory.config.DependencyDescriptor#resolveCandidate
public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory) throws BeansException {
return beanFactory.getBean(beanName);
}
对于单例类型的Bean,resolveCandidate函数会调用getBean指令以获取相应的Bean,getBean中的相关逻辑则会保证该Bean被实例化。
结论:DependencyObjectProvider在获取满足条件的单例Bean过程中,会对其进行实例化操作。