ObjectProvider

1.ObjectProvider<T>接口

Spring 4.3版本中新加入了ObjectProvider<T>接口,ObjectProvider<T>接口源码如下:

package org.springframework.beans.factory;import java.util.Iterator;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Stream;import org.springframework.beans.BeansException;
import org.springframework.lang.Nullable;/*** A variant of {@link ObjectFactory} designed specifically for injection points,* allowing for programmatic optionality and lenient not-unique handling.** <p>As of 5.1, this interface extends {@link Iterable} and provides {@link Stream}* support. It can be therefore be used in {@code for} loops, provides {@link #forEach}* iteration and allows for collection-style {@link #stream} access.** @author Juergen Hoeller* @since 4.3* @param <T> the object type* @see BeanFactory#getBeanProvider* @see org.springframework.beans.factory.annotation.Autowired*/
public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {/*** Return an instance (possibly shared or independent) of the object* managed by this factory.* <p>Allows for specifying explicit construction arguments, along the* lines of {@link BeanFactory#getBean(String, Object...)}.* @param args arguments to use when creating a corresponding instance* @return an instance of the bean* @throws BeansException in case of creation errors* @see #getObject()*/T getObject(Object... args) throws BeansException;/*** Return an instance (possibly shared or independent) of the object* managed by this factory.* @return an instance of the bean, or {@code null} if not available* @throws BeansException in case of creation errors* @see #getObject()*/@NullableT getIfAvailable() throws BeansException;/*** Return an instance (possibly shared or independent) of the object* managed by this factory.* @param defaultSupplier a callback for supplying a default object* if none is present in the factory* @return an instance of the bean, or the supplied default object* if no such bean is available* @throws BeansException in case of creation errors* @since 5.0* @see #getIfAvailable()*/default T getIfAvailable(Supplier<T> defaultSupplier) throws BeansException {T dependency = getIfAvailable();return (dependency != null ? dependency : defaultSupplier.get());}/*** Consume an instance (possibly shared or independent) of the object* managed by this factory, if available.* @param dependencyConsumer a callback for processing the target object* if available (not called otherwise)* @throws BeansException in case of creation errors* @since 5.0* @see #getIfAvailable()*/default void ifAvailable(Consumer<T> dependencyConsumer) throws BeansException {T dependency = getIfAvailable();if (dependency != null) {dependencyConsumer.accept(dependency);}}/*** Return an instance (possibly shared or independent) of the object* managed by this factory.* @return an instance of the bean, or {@code null} if not available or* not unique (i.e. multiple candidates found with none marked as primary)* @throws BeansException in case of creation errors* @see #getObject()*/@NullableT getIfUnique() throws BeansException;/*** Return an instance (possibly shared or independent) of the object* managed by this factory.* @param defaultSupplier a callback for supplying a default object* if no unique candidate is present in the factory* @return an instance of the bean, or the supplied default object* if no such bean is available or if it is not unique in the factory* (i.e. multiple candidates found with none marked as primary)* @throws BeansException in case of creation errors* @since 5.0* @see #getIfUnique()*/default T getIfUnique(Supplier<T> defaultSupplier) throws BeansException {T dependency = getIfUnique();return (dependency != null ? dependency : defaultSupplier.get());}/*** Consume an instance (possibly shared or independent) of the object* managed by this factory, if unique.* @param dependencyConsumer a callback for processing the target object* if unique (not called otherwise)* @throws BeansException in case of creation errors* @since 5.0* @see #getIfAvailable()*/default void ifUnique(Consumer<T> dependencyConsumer) throws BeansException {T dependency = getIfUnique();if (dependency != null) {dependencyConsumer.accept(dependency);}}/*** Return an {@link Iterator} over all matching object instances,* without specific ordering guarantees (but typically in registration order).* @since 5.1* @see #stream()*/@Overridedefault Iterator<T> iterator() {return stream().iterator();}/*** Return a sequential {@link Stream} over all matching object instances,* without specific ordering guarantees (but typically in registration order).* @since 5.1* @see #iterator()* @see #orderedStream()*/default Stream<T> stream() {throw new UnsupportedOperationException("Multi element access not supported");}/*** Return a sequential {@link Stream} over all matching object instances,* pre-ordered according to the factory's common order comparator.* <p>In a standard Spring application context, this will be ordered* according to {@link org.springframework.core.Ordered} conventions,* and in case of annotation-based configuration also considering the* {@link org.springframework.core.annotation.Order} annotation,* analogous to multi-element injection points of list/array type.* @since 5.1* @see #stream()* @see org.springframework.core.OrderComparator*/default Stream<T> orderedStream() {throw new UnsupportedOperationException("Ordered element access not supported");}}

看看官方提供的注释:A variant of {@link ObjectFactory} designed specifically for injection points,allowing for programmatic optionality and lenient not-unique handling.

注释的意思是:属于ObjectFactory<T>接口的一种变体,专为注入点设计,允许编程可选性和宽松的非唯一处理。描述非常抽象,下面详细解释ObjectProvider<T>接口的作用。


2.Spring构造函数隐式注入

Spring 4.3版本之前,如果使用构造函数的方式注入另外的一个Bean,必须显示依赖@Autowired注解,如下所示:

@Service
public class TestService {private final TestComponent testComponent;@Autowiredpublic TestService(TestComponent testComponent) {this.testComponent = testComponent;}}

这是非常常见的构造器注入方式。但是如果忘记添加构造函数上的@Autowired注解,启动时容器将抛出一个寻找默认构造函数的异常,除非在Bean定义设置中明确指出autowire模式为constructor模式。如在XML配置文件的<bean></bean>标签对中。

Spring 4.3版本开始,不再需要在上述这种单构造函数场景中指定显式注入所需的@Autowired注解。对于某些根本不带任何容器注释的类来说,这是特别优雅的。Spring容器在启动时,创建TestService这个Bean的时候,会从BeanFactory中查找TestComponent这个Bean并注入进来。

同时,@Configuration注解修饰的类不在历史上不支持构造函数注入。从Spring 4.3版本开始,也允许在单构造函数场景中省略@Autowired注解。

TestService

@Service
public class TestService {private final TestComponent testComponent;public TestService(TestComponent testComponent) {this.testComponent = testComponent;}}

TestConfiguration

@Configuration
public class TestConfiguration {private final TestComponent testComponent;public TestConfiguration(TestComponent testComponent) {this.testComponent = testComponent;}@Beanpublic TestService testService() {return new TestService(this.testComponent);}}

但是隐式注入也不是完美的,它存在强依赖,如果这个依赖不存在,就会发生这样的悲剧:

Parameter 0 of constructor in com.xxx.xxx.xxxx required a bean of type 'com.xxx.xxx.xxxx' that could not be found.

如何解决上述这种场景的问题,就要使用到本文介绍的ObjectProvider<T>接口。


3.Spring依赖关系的改进版编程

Spring 4.3中引入了ObjectProvider<T>接口。它是现有ObjectFactory<T>接口的扩展,具有方便的签名。例如getIfAvailable()getIfUnique()。只有在它实际存在时才会检索Bean,同时支持可选,可以确定单个候选者在多个匹配的Bean的情况下。

使用ObjectProvider<T>进行构造器注入,可以灵活选择合适的Bean进行注入:

@Service
public class TestService {private final TestComponent testComponent;public TestService(ObjectProvider<TestComponent> testComponentObjectProvider) {this.testComponent = testComponentObjectProvider.getIfUnique();}}

这样注入的好处很明显,如果容器中不存在testComponent或者存在多个testComponent时,可以从容处理。

Spring中依赖注入的方式有很多种,为什么要一直关注构造器注入,并且Spring官方推荐的依赖注入方式也是构造器注入?

如果依赖关系是强制的,那么最好使用构造函数进行注入。Spring官方这样推荐的理由是:

先来看看Spring官方文档中的描述:The Spring team generally advocates constructor injection as it enables one to implement application components as immutable objects and to ensure that required dependencies are not null. Furthermore constructor-injected components are always returned to client (calling) code in a fully initialized state.

大致意思就是:Spring团队通常提倡构造器注入,因为它可以将应用程序组件实现为不可变对象,并确保所需的依赖项不为空。此外,构造函数注入的组件总是以完全初始化的状态返回给客户端(调用)代码。

1️⃣单一职责原则:当使用构造函数注入依赖时,开发者很容易识别参数是否过多。此时就需要考虑这个类的职责是否过大,是否需要拆分等问题。而使用@Resource@Autowired注解注入时,不容易发现与识别这个问题。

2️⃣依赖不可变:使用构造器注入时,使用final关键字修饰field

3️⃣依赖不为空:使用构造器注入时,当要实例化一个Bean时,由于自己实现了有参构造函数,Spring不会调用默认构造函数,此时Spring容器注入所需的依赖,会存在两种情况:

  • 容器中存在所需要的Bean,直接注入。
  • 容器中不存在所需要的Bean,此时Spring会抛出异常。所以Spring会保证注入的依赖不为空。

4️⃣完全初始化状态:这个点可以根据上面依赖不为空结合起来。Spring调用构造器实例化某个Bean时,要确保注入的依赖不为空,那么肯定会调用所依赖组件的构造方法完成组件的实例化。所以Spring注入进来的依赖都是初始化后的状态。


4.ObjectProvider<T>源码分析

Spring 4.3后,使用构造器注入依赖不需要精确地指定@Autowired注解,毫无疑问是简化了开发,但是与此同时也带来了新的问题。在使用构造器注入依赖时,如果容器中不存在或者存在多个符合的Bean,此时Spring会抛出异常。为了解决这个问题,ObjectProvider<T>接口应运而生。使用ObjectProvider<T>则可以避免强依赖导致的依赖对象不存在异常。如果容器中存在多个类型相同的Bean实例,ObjectProvider<T>中提供的方法可以根据Bean实现的Ordered接口或者@Order注解指定的先后顺序获取一个Bean。这个过程的实现具体体现在org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency()方法中:

 @Override@Nullablepublic Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {// descriptor 代表当前需要注入的字段或者方法参数, 也就是注入点// ParameterNameDiscovery 用于解析方法参数名称descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());// 1.Optional<T>if (Optional.class == descriptor.getDependencyType()) {return createOptionalDependency(descriptor, requestingBeanName);}// 2.ObjectFactory<T>、ObjectProvider<T>else if (ObjectFactory.class == descriptor.getDependencyType() ||ObjectProvider.class == descriptor.getDependencyType()) {return new DependencyObjectProvider(descriptor, requestingBeanName);}// 3.javax.inject.Provider<T>else if (javaxInjectProviderClass == descriptor.getDependencyType()) {return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);}// 4.@Lazyelse {Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(descriptor, requestingBeanName);// 5.正常情况if (result == null) {result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);}return result;}}

可以看到,当注入点为ObjectFactory<T>或者ObjectProvider<T>时,会new一个DependencyObjectProvider返回出去,可以看看DependencyObjectProvider的继承关系:

BeanObjectProvider<T>

    private interface BeanObjectProvider<T> extends ObjectProvider<T>, Serializable {}

DependencyObjectProvider

    private class DependencyObjectProvider implements BeanObjectProvider<Object> {private final DependencyDescriptor descriptor;private final boolean optional;@Nullableprivate final String beanName;public DependencyObjectProvider(DependencyDescriptor descriptor, @Nullable String beanName) {this.descriptor = new NestedDependencyDescriptor(descriptor);this.optional = this.descriptor.getDependencyType() == Optional.class;this.beanName = beanName;}public Object getObject() throws BeansException {if (this.optional) {return DefaultListableBeanFactory.this.createOptionalDependency(this.descriptor, this.beanName);} else {Object result = DefaultListableBeanFactory.this.doResolveDependency(this.descriptor, this.beanName, (Set)null, (TypeConverter)null);if (result == null) {throw new NoSuchBeanDefinitionException(this.descriptor.getResolvableType());} else {return result;}}}public Object getObject(final Object... args) throws BeansException {if (this.optional) {return DefaultListableBeanFactory.this.createOptionalDependency(this.descriptor, this.beanName, args);} else {DependencyDescriptor descriptorToUse = new DependencyDescriptor(this.descriptor) {public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory) {return beanFactory.getBean(beanName, args);}};Object result = DefaultListableBeanFactory.this.doResolveDependency(descriptorToUse, this.beanName, (Set)null, (TypeConverter)null);if (result == null) {throw new NoSuchBeanDefinitionException(this.descriptor.getResolvableType());} else {return result;}}}@Nullablepublic Object getIfAvailable() throws BeansException {try {if (this.optional) {return DefaultListableBeanFactory.this.createOptionalDependency(this.descriptor, this.beanName);} else {DependencyDescriptor descriptorToUse = new DependencyDescriptor(this.descriptor) {public boolean isRequired() {return false;}};return DefaultListableBeanFactory.this.doResolveDependency(descriptorToUse, this.beanName, (Set)null, (TypeConverter)null);}} catch (ScopeNotActiveException var2) {return null;}}public void ifAvailable(Consumer<Object> dependencyConsumer) throws BeansException {Object dependency = this.getIfAvailable();if (dependency != null) {try {dependencyConsumer.accept(dependency);} catch (ScopeNotActiveException var4) {}}}@Nullablepublic Object getIfUnique() throws BeansException {DependencyDescriptor descriptorToUse = new DependencyDescriptor(this.descriptor) {public boolean isRequired() {return false;}@Nullablepublic Object resolveNotUnique(ResolvableType type, Map<String, Object> matchingBeans) {return null;}};try {return this.optional ? DefaultListableBeanFactory.this.createOptionalDependency(descriptorToUse, this.beanName) : DefaultListableBeanFactory.this.doResolveDependency(descriptorToUse, this.beanName, (Set)null, (TypeConverter)null);} catch (ScopeNotActiveException var3) {return null;}}public void ifUnique(Consumer<Object> dependencyConsumer) throws BeansException {Object dependency = this.getIfUnique();if (dependency != null) {try {dependencyConsumer.accept(dependency);} catch (ScopeNotActiveException var4) {}}}@Nullableprotected Object getValue() throws BeansException {return this.optional ? DefaultListableBeanFactory.this.createOptionalDependency(this.descriptor, this.beanName) : DefaultListableBeanFactory.this.doResolveDependency(this.descriptor, this.beanName, (Set)null, (TypeConverter)null);}public Stream<Object> stream() {return this.resolveStream(false);}public Stream<Object> orderedStream() {return this.resolveStream(true);}private Stream<Object> resolveStream(boolean ordered) {DependencyDescriptor descriptorToUse = new StreamDependencyDescriptor(this.descriptor, ordered);Object result = DefaultListableBeanFactory.this.doResolveDependency(descriptorToUse, this.beanName, (Set)null, (TypeConverter)null);return result instanceof Stream ? (Stream)result : Stream.of(result);}}

可以看看DependencyObjectProvider中的getIfAvailable()方法:

     @Override@Nullablepublic Object getIfAvailable() throws BeansException {try {// 用于解决嵌套的情况, 类似于ObjectProvider<Optional<T>>if (this.optional) {return createOptionalDependency(this.descriptor, this.beanName);}else {DependencyDescriptor descriptorToUse = new DependencyDescriptor(this.descriptor) {@Overridepublic boolean isRequired() {return false;}};// 最终还是会调用doResolveDependency()方法解决依赖return doResolveDependency(descriptorToUse, this.beanName, null, null);}}catch (ScopeNotActiveException ex) {// Ignore resolved bean in non-active scopereturn null;}}

Spring核心接口ObjectProvider相关推荐

  1. 一文带你了解Spring核心接口Ordered的实现及应用

    前言 最近在看框架的时候,发现了这个接口,在此进行总结,希望能够给大家帮助,同时提升自己. order接口的大体介绍 Spring框架中有这个一个接口,名字叫Ordered,联想我们在数据库中应用的O ...

  2. Spring核心接口之Ordered

    一.Ordered接口介绍 Spring中提供了一个Ordered接口.从单词意思就知道Ordered接口的作用就是用来排序的. Spring框架是一个大量使用策略设计模式的框架,这意味着有很多相同接 ...

  3. spring中基础核心接口总结

    spring中基础核心接口总结 理解这几个接口,及其实现类就可以快速了解spring,具体的用法参考其他spring资料 1.BeanFactory最基础最核心的接口 重要的实现类有: XmlBean ...

  4. Spring : Spring事物支持核心接口

    1.美图 2.核心接口概览 3.TransactionDefinition TransactionDefinition–>定义与spring兼容的事务属性的接口 public interface ...

  5. Spring AOP(二)AOPAlliance与SpringAOP核心接口介绍

    目录 AOP联盟 1. Advice.MethodInterceptor拦截器(invoke方法:调用invocation.proceed) 2.Joinpoint .MethodInvocation ...

  6. spring核心功能结构

    spring核心功能结构 Spring大约有20个模块,由1300多个不同的文件构成.这些模块可以分为: 核心容器.AOP和设备支持.数据访问与集成.Web组件.通信报文和集成测试等, 下面是 Spr ...

  7. Spring核心——IOC处理器扩展

    为什么80%的码农都做不了架构师?>>>    非侵入式框架 Spring一直标注自己是一个非侵入式框架.非侵入式设计的概念并不新鲜,目标就是降低使用者和框架代码的耦合,毕竟框架的开 ...

  8. Spring核心之对 IOC的理解

    Spring核心之对 IOC的理解 Spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器框架. 一 .IOC IOC : Inversion of Control,中文译为" ...

  9. Spring.ImportSelector接口

    一.关于ImportSelector接口 ImportSelector接口是至spring中导入外部配置的核心接口,在SpringBoot的自动化配置和@EnableXXX(功能性注解)都有它的存在 ...

  10. Spring核心注释

    介绍: org.springframework.beans.factory.annotation和org.springframework.context.annotation包中存在的Spring注释 ...

最新文章

  1. AAAI2021 | 图神经网络最新进展解读
  2. Binder相关面试总结(六):四大组件底层的通信机制是怎样的
  3. Android补间动画笔记
  4. vim 记录阅读信息
  5. gitlab永久设置密码
  6. Tomcat Caused by java lang OutOfMemoryError PermGen space
  7. python爬取+BI分析5000条内衣数据,发现妹子最爱这款文胸
  8. matlab设置ga算法,matlab遗传算法ga函数
  9. linux下包管理工具apt-get
  10. PHP、JS、Python,数据库 获取今天是星期几了?[开发篇]
  11. 白杨SEO:百度移动搜索上百度笔记是什么、收录规则及排名怎么做?
  12. 40几岁读研究生计算机,年近四十岁,还有必要去考研和继续考博吗?不建议考全日制研究生...
  13. 2017-03-27Oracle故障gc buffer busy acquire导致数据库不可用
  14. What Is New About NewSQL(NewSQL的独到之处)?
  15. 詹姆斯·高斯林-JAVA之父
  16. Rust中,*const T和*mut T的区别是什么?
  17. SonicWALL防火墙通过策略指定WAN口访问网络
  18. SNS游戏开发的感想
  19. VML实现的饼图(JavaScript类封装)
  20. 硬件-----电路基础(上)

热门文章

  1. 11 Django REST Framework 针对基于类的视图添加 @csrf_exempt
  2. java全能速查宝典.chm_Java全能速查宝典
  3. 《给青年的十二封信》2-谈动—朱光潜
  4. 编程语言c语言程序包括的几种语句
  5. 远程访问openwrt路由器+配置动态DNS
  6. 1753: [Usaco2005 qua]Who's in the Middle (快速排序)
  7. windows无法连接到某个wifi_电脑提示Windows无法连接到这个网络/无线网络的解决方法...
  8. 计算机视觉论文-2021-06-10
  9. JetBrain 系列软件快捷键集合
  10. JetBrain的哪款产品能够编写C++和C的代码啊?