Spring核心接口ObjectProvider
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相关推荐
- 一文带你了解Spring核心接口Ordered的实现及应用
前言 最近在看框架的时候,发现了这个接口,在此进行总结,希望能够给大家帮助,同时提升自己. order接口的大体介绍 Spring框架中有这个一个接口,名字叫Ordered,联想我们在数据库中应用的O ...
- Spring核心接口之Ordered
一.Ordered接口介绍 Spring中提供了一个Ordered接口.从单词意思就知道Ordered接口的作用就是用来排序的. Spring框架是一个大量使用策略设计模式的框架,这意味着有很多相同接 ...
- spring中基础核心接口总结
spring中基础核心接口总结 理解这几个接口,及其实现类就可以快速了解spring,具体的用法参考其他spring资料 1.BeanFactory最基础最核心的接口 重要的实现类有: XmlBean ...
- Spring : Spring事物支持核心接口
1.美图 2.核心接口概览 3.TransactionDefinition TransactionDefinition–>定义与spring兼容的事务属性的接口 public interface ...
- Spring AOP(二)AOPAlliance与SpringAOP核心接口介绍
目录 AOP联盟 1. Advice.MethodInterceptor拦截器(invoke方法:调用invocation.proceed) 2.Joinpoint .MethodInvocation ...
- spring核心功能结构
spring核心功能结构 Spring大约有20个模块,由1300多个不同的文件构成.这些模块可以分为: 核心容器.AOP和设备支持.数据访问与集成.Web组件.通信报文和集成测试等, 下面是 Spr ...
- Spring核心——IOC处理器扩展
为什么80%的码农都做不了架构师?>>> 非侵入式框架 Spring一直标注自己是一个非侵入式框架.非侵入式设计的概念并不新鲜,目标就是降低使用者和框架代码的耦合,毕竟框架的开 ...
- Spring核心之对 IOC的理解
Spring核心之对 IOC的理解 Spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器框架. 一 .IOC IOC : Inversion of Control,中文译为" ...
- Spring.ImportSelector接口
一.关于ImportSelector接口 ImportSelector接口是至spring中导入外部配置的核心接口,在SpringBoot的自动化配置和@EnableXXX(功能性注解)都有它的存在 ...
- Spring核心注释
介绍: org.springframework.beans.factory.annotation和org.springframework.context.annotation包中存在的Spring注释 ...
最新文章
- AAAI2021 | 图神经网络最新进展解读
- Binder相关面试总结(六):四大组件底层的通信机制是怎样的
- Android补间动画笔记
- vim 记录阅读信息
- gitlab永久设置密码
- Tomcat Caused by java lang OutOfMemoryError PermGen space
- python爬取+BI分析5000条内衣数据,发现妹子最爱这款文胸
- matlab设置ga算法,matlab遗传算法ga函数
- linux下包管理工具apt-get
- PHP、JS、Python,数据库 获取今天是星期几了?[开发篇]
- 白杨SEO:百度移动搜索上百度笔记是什么、收录规则及排名怎么做?
- 40几岁读研究生计算机,年近四十岁,还有必要去考研和继续考博吗?不建议考全日制研究生...
- 2017-03-27Oracle故障gc buffer busy acquire导致数据库不可用
- What Is New About NewSQL(NewSQL的独到之处)?
- 詹姆斯·高斯林-JAVA之父
- Rust中,*const T和*mut T的区别是什么?
- SonicWALL防火墙通过策略指定WAN口访问网络
- SNS游戏开发的感想
- VML实现的饼图(JavaScript类封装)
- 硬件-----电路基础(上)
热门文章
- 11 Django REST Framework 针对基于类的视图添加 @csrf_exempt
- java全能速查宝典.chm_Java全能速查宝典
- 《给青年的十二封信》2-谈动—朱光潜
- 编程语言c语言程序包括的几种语句
- 远程访问openwrt路由器+配置动态DNS
- 1753: [Usaco2005 qua]Who's in the Middle (快速排序)
- windows无法连接到某个wifi_电脑提示Windows无法连接到这个网络/无线网络的解决方法...
- 计算机视觉论文-2021-06-10
- JetBrain 系列软件快捷键集合
- JetBrain的哪款产品能够编写C++和C的代码啊?