Springboot Objectprovider性质小探

目录

引例:HTTPMessageConverters与Objectprovider

试用Objectprovider

ObjectProvider与DependOn注解

源码分析


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过程中,会对其进行实例化操作。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
package com.xiangyixiang.www.cloud.business.gateway.configure; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.http.HttpMessageConverters; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.reactive.CorsWebFilter; import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource; import org.springframework.web.util.pattern.PathPatternParser; import java.util.stream.Collectors; /**网关统一配置允许跨域 * zj 解决跨域问题 * @author 张静 */ @Configuration public class CorsConfig { @Bean public CorsWebFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); config.addAllowedMethod("*"); config.addAllowedOrigin("http://39.105.36.172:9250"); config.addAllowedHeader("*"); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser()); source.registerCorsConfiguration("/**", config); return new CorsWebFilter(source); } @Bean @ConditionalOnMissingBean public HttpMessageConverters messageConverters(ObjectProvider<HttpMessageConverter<?>> converters) { return new HttpMessageConverters(converters.orderedStream().collect(Collectors.toList())); } }
06-10

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值