Declare complete set of default methods on ObjectProvider
Closes gh-33070
This commit is contained in:
parent
4cbaaa3b1d
commit
1047e1f722
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -22,16 +22,27 @@ import java.util.function.Supplier;
|
|||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.core.OrderComparator;
|
||||
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>In a {@link BeanFactory} environment, every {@code ObjectProvider} obtained
|
||||
* from the factory will be bound to its {@code BeanFactory} for a specific bean
|
||||
* type, matching all provider calls against factory-registered bean definitions.
|
||||
*
|
||||
* <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.
|
||||
*
|
||||
* <p>As of 6.2, this interface declares default implementations for all methods.
|
||||
* This makes it easier to implement in a custom fashion, e.g. for unit tests.
|
||||
* For typical purposes, implement {@link #stream()} to enable all other methods.
|
||||
* Alternatively, you may implement the specific methods that your callers expect,
|
||||
* e.g. just {@link #getObject()} or {@link #getIfAvailable()}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.3
|
||||
* @param <T> the object type
|
||||
|
|
@ -40,6 +51,19 @@ import org.springframework.lang.Nullable;
|
|||
*/
|
||||
public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {
|
||||
|
||||
@Override
|
||||
default T getObject() throws BeansException {
|
||||
Iterator<T> it = iterator();
|
||||
if (!it.hasNext()) {
|
||||
throw new NoSuchBeanDefinitionException(Object.class);
|
||||
}
|
||||
T result = it.next();
|
||||
if (it.hasNext()) {
|
||||
throw new NoUniqueBeanDefinitionException(Object.class, 2, "more than 1 matching bean");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an instance (possibly shared or independent) of the object
|
||||
* managed by this factory.
|
||||
|
|
@ -50,7 +74,10 @@ public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {
|
|||
* @throws BeansException in case of creation errors
|
||||
* @see #getObject()
|
||||
*/
|
||||
T getObject(Object... args) throws BeansException;
|
||||
default T getObject(Object... args) throws BeansException {
|
||||
throw new UnsupportedOperationException("Retrieval with arguments not supported -" +
|
||||
"for custom ObjectProvider classes, implement getObject(Object...) for your purposes");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an instance (possibly shared or independent) of the object
|
||||
|
|
@ -60,7 +87,17 @@ public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {
|
|||
* @see #getObject()
|
||||
*/
|
||||
@Nullable
|
||||
T getIfAvailable() throws BeansException;
|
||||
default T getIfAvailable() throws BeansException {
|
||||
try {
|
||||
return getObject();
|
||||
}
|
||||
catch (NoUniqueBeanDefinitionException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an instance (possibly shared or independent) of the object
|
||||
|
|
@ -103,7 +140,14 @@ public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {
|
|||
* @see #getObject()
|
||||
*/
|
||||
@Nullable
|
||||
T getIfUnique() throws BeansException;
|
||||
default T getIfUnique() throws BeansException {
|
||||
try {
|
||||
return getObject();
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an instance (possibly shared or independent) of the object
|
||||
|
|
@ -157,7 +201,8 @@ public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {
|
|||
* @see #orderedStream()
|
||||
*/
|
||||
default Stream<T> stream() {
|
||||
throw new UnsupportedOperationException("Multi element access not supported");
|
||||
throw new UnsupportedOperationException("Element access not supported - " +
|
||||
"for custom ObjectProvider classes, implement stream() to enable all other methods");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -168,12 +213,16 @@ public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {
|
|||
* 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.
|
||||
* <p>The default method applies an {@link OrderComparator} to the
|
||||
* {@link #stream()} method. You may override this to apply an
|
||||
* {@link org.springframework.core.annotation.AnnotationAwareOrderComparator}
|
||||
* if necessary.
|
||||
* @since 5.1
|
||||
* @see #stream()
|
||||
* @see org.springframework.core.OrderComparator
|
||||
*/
|
||||
default Stream<T> orderedStream() {
|
||||
throw new UnsupportedOperationException("Ordered element access not supported");
|
||||
return stream().sorted(OrderComparator.INSTANCE);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -37,7 +37,6 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
|||
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.SmartFactoryBean;
|
||||
import org.springframework.core.OrderComparator;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
|
@ -344,10 +343,6 @@ public class StaticListableBeanFactory implements ListableBeanFactory {
|
|||
public Stream<T> stream() {
|
||||
return Arrays.stream(getBeanNamesForType(requiredType)).map(name -> (T) getBean(name));
|
||||
}
|
||||
@Override
|
||||
public Stream<T> orderedStream() {
|
||||
return stream().sorted(OrderComparator.INSTANCE);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.beans.factory;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.testfixture.beans.TestBean;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* @author Juergen Hoeller
|
||||
* @since 6.2
|
||||
*/
|
||||
public class CustomObjectProviderTests {
|
||||
|
||||
@Test
|
||||
void getObject() {
|
||||
TestBean tb1 = new TestBean("tb1");
|
||||
|
||||
ObjectProvider<TestBean> provider = new ObjectProvider<>() {
|
||||
@Override
|
||||
public TestBean getObject() throws BeansException {
|
||||
return tb1;
|
||||
}
|
||||
};
|
||||
|
||||
assertThat(provider.getObject()).isSameAs(tb1);
|
||||
assertThat(provider.getIfAvailable()).isSameAs(tb1);
|
||||
assertThat(provider.getIfUnique()).isSameAs(tb1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void noObject() {
|
||||
ObjectProvider<TestBean> provider = new ObjectProvider<>() {
|
||||
@Override
|
||||
public TestBean getObject() throws BeansException {
|
||||
throw new NoSuchBeanDefinitionException(Object.class);
|
||||
}
|
||||
};
|
||||
|
||||
assertThatExceptionOfType(NoSuchBeanDefinitionException.class).isThrownBy(provider::getObject);
|
||||
assertThat(provider.getIfAvailable()).isNull();
|
||||
assertThat(provider.getIfUnique()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void noUniqueObject() {
|
||||
ObjectProvider<TestBean> provider = new ObjectProvider<>() {
|
||||
@Override
|
||||
public TestBean getObject() throws BeansException {
|
||||
throw new NoUniqueBeanDefinitionException(Object.class);
|
||||
}
|
||||
};
|
||||
|
||||
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class).isThrownBy(provider::getObject);
|
||||
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class).isThrownBy(provider::getIfAvailable);
|
||||
assertThat(provider.getIfUnique()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void emptyStream() {
|
||||
ObjectProvider<TestBean> provider = new ObjectProvider<>() {
|
||||
@Override
|
||||
public Stream<TestBean> stream() {
|
||||
return Stream.empty();
|
||||
}
|
||||
};
|
||||
|
||||
assertThatExceptionOfType(NoSuchBeanDefinitionException.class).isThrownBy(provider::getObject);
|
||||
assertThat(provider.getIfAvailable()).isNull();
|
||||
assertThat(provider.getIfUnique()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void streamWithOneObject() {
|
||||
TestBean tb1 = new TestBean("tb1");
|
||||
|
||||
ObjectProvider<TestBean> provider = new ObjectProvider<>() {
|
||||
@Override
|
||||
public Stream<TestBean> stream() {
|
||||
return Stream.of(tb1);
|
||||
}
|
||||
};
|
||||
|
||||
assertThat(provider.getObject()).isSameAs(tb1);
|
||||
assertThat(provider.getIfAvailable()).isSameAs(tb1);
|
||||
assertThat(provider.getIfUnique()).isSameAs(tb1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void streamWithTwoObjects() {
|
||||
TestBean tb1 = new TestBean("tb1");
|
||||
TestBean tb2 = new TestBean("tb2");
|
||||
|
||||
ObjectProvider<TestBean> provider = new ObjectProvider<>() {
|
||||
@Override
|
||||
public Stream<TestBean> stream() {
|
||||
return Stream.of(tb1, tb2);
|
||||
}
|
||||
};
|
||||
|
||||
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class).isThrownBy(provider::getObject);
|
||||
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class).isThrownBy(provider::getIfAvailable);
|
||||
assertThat(provider.getIfUnique()).isNull();
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue