首页 > 其他分享 >Spring—ObjectProvider更加宽泛的依赖注入

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

时间:2023-08-07 15:35:25浏览次数:44  
标签:timeoutInterceptor Spring factory 宽泛 dependency bean ObjectProvider public

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。

这种方式本质还是通过bean的id或者name来获取bean,通过第二个参数Object[] args可以给bean的属性赋值,赋值的方式有两种:构造方法和工厂方法。但是通过这种方式获取的bean必须把scope属性设置为prototype,也就是非单例模式。

推荐阅读

Spring Boot 注解之ObjectProvider源码追踪


标签:timeoutInterceptor,Spring,factory,宽泛,dependency,bean,ObjectProvider,public
From: https://blog.51cto.com/u_15668812/6994194

相关文章

  • springboot智能3D导诊系统源码,基于规则模板的开发原理
    互联网智慧3D导诊系统源码通过智能导诊,进行自助问询及挂号服务,减轻导诊台护士压力,挂号更加方便快捷。技术架构:springboot+redis+mybatisplus+mysql+RocketMQ  智慧导诊系统开发原理导诊系统从原理上大致可分为基于规则模板和基于数据模型两类。1、基于规则推理的方法通过人工建......
  • Spring记录-01
    一、Spring三种思想1.loc思想:InversionofControl,控制反转,强调是在原来程序中创建中创建Bean的权利反转给第三方2.Dl思想:DependencyInjection,依赖注入,强调Bean之间的关系,这种关系由第三方去负责并管理3.AOP思想:AspectOrientedProgramming,面向切面编程,功能的横向抽取,主......
  • Springboot-Mybatis(idea)-自学笔记
    Spring-boot-Mybaties快速入门使用Mybatis查询所有用户数据准备工作(创建springboot工程,数据库表格user,实体类User)引入Mybatis的相关依赖,配置Mybatis(数据库连接信息)编写SQL语句(注解/XML)单元测试packagecom.example;importcom.example.mapper.UserMapper;impo......
  • SpringBoot3基础用法
    技术和工具「!喜新厌旧」一、背景最近在一个轻量级的服务中,尝试了最新的技术和工具选型;即SpringBoot3,JDK17,IDEA2023,Navicat16,虽然新的技术和工具都更加强大和高效,但是适应采坑的过程总是枯燥的;【环境一览】首先框架主体从SpringBoot2升级到SpringBoot3,Java基础环境从JDK8升......
  • Spring Boot是什么?它的优缺点以及核心
    一、SpringBoot是什么?SpringBoot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Boot致力于在蓬勃发展的快速应用开发领域(rapidapplicationd......
  • SpringBoot3基础用法
    目录一、背景二、环境搭建1、工程结构2、框架依赖3、环境配置三、入门案例1、测试接口2、全局异常3、日志打印3.1日志配置3.2日志打印四、打包运行五、参考源码技术和工具「!喜新厌旧」一、背景最近在一个轻量级的服务中,尝试了最新的技术和工具选型;即SpringBoot3,JDK17,IDEA......
  • SpringBoot Netty socket使用
    SpringBootNettysocket使用Netty是由JBOSS提供的一个java开源框架,现为Github上的独立项目。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。这里springBoot整合起来使用测试,性能怎么的不怎么了解,至少能用maven引用依......
  • 如何解决Spring循环依赖?
    在Spring中,循环依赖指的是两个或多个Bean之间相互依赖,形成一个闭环,导致Spring容器无法正确地创建这些Bean。解决Spring循环依赖的方法是通过Spring容器的三个阶段来处理:实例化、属性注入、和初始化。为了更好地解释,我们假设有两个类A和B相互依赖。A依赖于B,B也依赖于A。以下是解决循......
  • Spring Boot 最佳实践
    本文翻译自国外论坛medium,原文地址:https://medium.com/@raviyasas/spring-boot-best-practices-for-developers-3f3bdffa0090SpringBoot是一种广泛使用且非常流行的企业级高性能框架。以下是一些最佳实践和一些技巧,我们可以使用它们来改进SpringBoot应用程序并使其更加高效......
  • Spring常见问题
    一、Spring是什么Spring是一个轻量级的IOC和AOP容器框架。是为Java应用程序提供基础服务的一套框架,目的是简化企业应用程序的开发,它使得开发者只需要关心业务需求。主要包含以下七个模块:1.SpringContext:提供框架式的Bean访问方式,以及企业级功能;2.SpringCore:核心类库,所有功能都......