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);
}
}
}
注意点:
-
T getObject(Object... args)
的参数含义可参考ApplicationContext之getBean方法详解。即若是设置args参数获取bean。
这种方式本质还是通过bean的id或者name来获取bean,通过第二个参数Object[] args可以给bean的属性赋值,赋值的方式有两种:构造方法和工厂方法。但是通过这种方式获取的bean必须把scope属性设置为prototype,也就是非单例模式。
推荐阅读
Spring Boot 注解之ObjectProvider源码追踪