0
点赞
收藏
分享

微信扫一扫

Spring—ObjectProvider更加宽泛的依赖注入

1. Spring依赖注入

在Spring4.3之后,引入了一个新特性:当构造方法只有一个参数时,可以不使用@ Autowired注解。

@Service
public class FooService {

    private  FooRepository fooRepository;

    public FooService(FooRepository fooRepository){
        this.fooRepository=fooRepository;
    }
}

在SpringBoot的自动装配类中,这种形式被大量使用。

在Spring4.3版本,引入了ObjectProvider接口。

1.1 ObjectProvider的作用

  • 如果注入实例为空时,使用ObjectProvider则避免了强依赖导致的依赖对象不存在异常;
  • 如果有多个实例,ObjectProvider的方法会根据Bean实现的Ordered接口或@Order注解指定的先后顺序获取一个Bean。从而了提供了一个更加宽松的依赖注入方式。

可以看做是依赖注入的懒加载,异常将从启动阶段转移到业务运行的阶段。

@Service
public class FooService {
    private FooRepository fooRepository;
    public FooService(ObjectProvider<FooRepository> fooRepositoryObjectProvider){
        this.fooRepository=fooRepositoryObjectProvider.getIfAvailable();
    }
}

在Spring5.1版本后提供了基于orderedStream方法来获取有序的Stream方法。

@Service
public class FooService {
    private FooRepository fooRepository;
    public FooService(ObjectProvider<FooRepository> fooRepositoryObjectProvider){
        //可以灵活的选择注入的bean
        this.fooRepository=fooRepositoryObjectProvider.orderedStream().findFirst().get();
    }
}

1.2 ObjectProvider的实际运用场景

在某些业务中,我们需要这样一种场景:若配置文件配置了某些参数,那么便启用某个功能。

SpringBoot为我们提供了@ConditionalOnProperty(prefix = "xxx.rabbit", name = "thresholdTime")注解

例如:配置了一个拦截器,只有在yml文件中含有xxx.rabbit.thresholdTime的配置时,才会将timeoutInterceptor这个bean放入到Spring容器中。

@Slf4j
@Configuration
public class RabbitInterceptorAutoConfiguration {

    @Autowired
    private ApplicationContext applicationContext;

    @Bean
    @ConditionalOnProperty(prefix = "xxx.rabbit", name = "thresholdTime")
    @ConditionalOnMissingBean
    public TimeoutInterceptor timeoutInterceptor(XxxRabbitProperty property) {
        TimeoutInterceptor timeoutInterceptor = new TimeoutInterceptor();
        timeoutInterceptor.setThresholdTime(property.getThresholdTime());
        timeoutInterceptor.setApplicationContext(applicationContext);
        return timeoutInterceptor;
    }
}

但是若没有配置的话,Spring容器中没有该Bean,那么如何宽松的注入呢?

@Slf4j
@EnableRabbit
@Configuration
@EnableConfigurationProperties(value =XxxRabbitProperty.class)
public class RabbitConfiguration {

    @Autowired
    private ObjectProvider<TimeoutInterceptor> timeoutInterceptorObjectProvider;

    @Bean(name = "singleListenerContainer")
    public SimpleRabbitListenerContainerFactory listenerContainerFactory(CachingConnectionFactory connectionFactory) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setConcurrentConsumers(1);
        factory.setMaxConcurrentConsumers(10);
        factory.setPrefetchCount(250);
        /* 设置当rabbitmq收到nack/reject确认信息时的处理方式,设为true,扔回queue头部,设为false,丢弃。 */
        factory.setDefaultRequeueRejected(true);
        //慢消息触发事件通知
        List<Advice> adviceList = new ArrayList<>();
        //运行的时候才会判断是否存在该Bean。
        TimeoutInterceptor timeoutInterceptor = timeoutInterceptorObjectProvider.getIfAvailable();
        //拦截器
        if (timeoutInterceptor != null) {
            adviceList.add(timeoutInterceptor);
        }
        factory.setAdviceChain(adviceList.toArray(new Advice[adviceList.size()]));
        //自动确认
        factory.setAcknowledgeMode(AcknowledgeMode.AUTO);
        return factory;
    }
}

1.3 相关API

public interface ObjectProvider<T> extends ObjectFactory<T> {

     //返回指定类型的Bean。
     //如果容器中不存在,那么抛出NoSuchBeanDefinitionException异常;
     //如果容器中存在多个此类型的bean,抛出NoUniqueBeanDefinitionException异常。
    T getObject(Object... args) throws BeansException;

    //如果指定类型的bean注册到容器中,返回bean实例,否则返回null。
   //当存在多个实例时,抛出NoUniqueBeanDefinitionException异常
    @Nullable
    T getIfAvailable() throws BeansException;

    //Spring5.0后的方法
    // 如果返回对象不存在,则进行回调,回调对象由Supplier传入
    default T getIfAvailable(Supplier<T> defaultSupplier) throws BeansException {
        T dependency = getIfAvailable();
        return (dependency != null ? dependency : defaultSupplier.get());
    }

     //Spring5.0方法
      // 若bean存在,可以回调处理(消费)该Bean
    default void ifAvailable(Consumer<T> dependencyConsumer) throws BeansException {
        T dependency = getIfAvailable();
        if (dependency != null) {
            dependencyConsumer.accept(dependency);
        }
    }

      //如果不可用或不唯一(没有指定primary)则返回null。否则,返回对象。
    @Nullable
    T getIfUnique() throws BeansException;

    // 如果存在唯一对象,则调用Supplier的回调函数
    default T getIfUnique(Supplier<T> defaultSupplier) throws BeansException {
        T dependency = getIfUnique();
        return (dependency != null ? dependency : defaultSupplier.get());
    }

    // 如果存在唯一对象,则消费掉该对象
    default void ifUnique(Consumer<T> dependencyConsumer) throws BeansException {
        T dependency = getIfUnique();
        if (dependency != null) {
            dependencyConsumer.accept(dependency);
        }
    }
}

注意点:

  1. T getObject(Object... args)的参数含义可参考ApplicationContext之getBean方法详解。即若是设置args参数获取bean。

推荐阅读

Spring Boot 注解之ObjectProvider源码追踪

举报

相关推荐

0 条评论