Programmatic ObjectProvider retrieval through BeanFactory API
Introduces getBeanProvider(Class) and getBeanProvider(ResolvableType), also narrowing getBean(String, Class) and isTypeMatch(String, Class) to a non-null Class argument and enriching NoUniqueBeanDefinitionException with a full ResolvableType. In addition, ObjectProvider supports iterable/stream access for collection-style resolution of multiple matching beans now, and collection injection falls back to an empty collection in a single-constructor case with non-null arguments. Issue: SPR-17075 Issue: SPR-11419 Issue: SPR-15338
This commit is contained in:
@@ -35,6 +35,7 @@ import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.CachedIntrospectionResults;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
@@ -1083,7 +1084,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException {
|
||||
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
|
||||
assertBeanFactoryActive();
|
||||
return getBeanFactory().getBean(name, requiredType);
|
||||
}
|
||||
@@ -1106,6 +1107,18 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
return getBeanFactory().getBean(requiredType, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType) {
|
||||
assertBeanFactoryActive();
|
||||
return getBeanFactory().getBeanProvider(requiredType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType) {
|
||||
assertBeanFactoryActive();
|
||||
return getBeanFactory().getBeanProvider(requiredType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsBean(String name) {
|
||||
return getBeanFactory().containsBean(name);
|
||||
@@ -1130,7 +1143,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException {
|
||||
public boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException {
|
||||
assertBeanFactoryActive();
|
||||
return getBeanFactory().isTypeMatch(name, typeToMatch);
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.jndi.JndiLocatorSupport;
|
||||
import org.springframework.jndi.TypeMismatchNamingException;
|
||||
@@ -107,7 +109,7 @@ public class SimpleJndiBeanFactory extends JndiLocatorSupport implements BeanFac
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException {
|
||||
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
|
||||
try {
|
||||
if (isSingleton(name)) {
|
||||
return doGetSingleton(name, requiredType);
|
||||
@@ -150,6 +152,49 @@ public class SimpleJndiBeanFactory extends JndiLocatorSupport implements BeanFac
|
||||
return getBean(requiredType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType) {
|
||||
return new ObjectProvider<T>() {
|
||||
@Override
|
||||
public T getObject() throws BeansException {
|
||||
return getBean(requiredType);
|
||||
}
|
||||
@Override
|
||||
public T getObject(Object... args) throws BeansException {
|
||||
return getBean(requiredType, args);
|
||||
}
|
||||
@Override
|
||||
@Nullable
|
||||
public T getIfAvailable() throws BeansException {
|
||||
try {
|
||||
return getBean(requiredType);
|
||||
}
|
||||
catch (NoUniqueBeanDefinitionException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
@Nullable
|
||||
public T getIfUnique() throws BeansException {
|
||||
try {
|
||||
return getBean(requiredType);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType) {
|
||||
throw new UnsupportedOperationException(
|
||||
"SimpleJndiBeanFactory does not support resolution by ResolvableType");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsBean(String name) {
|
||||
if (this.singletonObjects.containsKey(name) || this.resourceTypes.containsKey(name)) {
|
||||
|
||||
@@ -44,12 +44,6 @@ import static org.springframework.util.StringUtils.*;
|
||||
*/
|
||||
public class AnnotationConfigApplicationContextTests {
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void nullGetBeanParameterIsDisallowed() {
|
||||
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
|
||||
context.getBean((Class<?>) null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scanAndRefresh() {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
|
||||
@@ -801,6 +801,40 @@ public class ConfigurationClassPostProcessorTests {
|
||||
assertSame(ctx.getBean(BarImpl.class), ctx.getBean(FooImpl.class).bar);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCollectionArgumentOnBeanMethod() {
|
||||
ApplicationContext ctx = new AnnotationConfigApplicationContext(CollectionArgumentConfiguration.class, TestBean.class);
|
||||
CollectionArgumentConfiguration bean = ctx.getBean(CollectionArgumentConfiguration.class);
|
||||
assertNotNull(bean.testBeans);
|
||||
assertEquals(1, bean.testBeans.size());
|
||||
assertSame(ctx.getBean(TestBean.class), bean.testBeans.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyCollectionArgumentOnBeanMethod() {
|
||||
ApplicationContext ctx = new AnnotationConfigApplicationContext(CollectionArgumentConfiguration.class);
|
||||
CollectionArgumentConfiguration bean = ctx.getBean(CollectionArgumentConfiguration.class);
|
||||
assertNotNull(bean.testBeans);
|
||||
assertTrue(bean.testBeans.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapArgumentOnBeanMethod() {
|
||||
ApplicationContext ctx = new AnnotationConfigApplicationContext(MapArgumentConfiguration.class, DummyRunnable.class);
|
||||
MapArgumentConfiguration bean = ctx.getBean(MapArgumentConfiguration.class);
|
||||
assertNotNull(bean.testBeans);
|
||||
assertEquals(1, bean.testBeans.size());
|
||||
assertSame(ctx.getBean(Runnable.class), bean.testBeans.values().iterator().next());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyMapArgumentOnBeanMethod() {
|
||||
ApplicationContext ctx = new AnnotationConfigApplicationContext(MapArgumentConfiguration.class);
|
||||
MapArgumentConfiguration bean = ctx.getBean(MapArgumentConfiguration.class);
|
||||
assertNotNull(bean.testBeans);
|
||||
assertTrue(bean.testBeans.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCollectionInjectionFromSameConfigurationClass() {
|
||||
ApplicationContext ctx = new AnnotationConfigApplicationContext(CollectionInjectionConfiguration.class);
|
||||
@@ -1564,6 +1598,43 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
}
|
||||
|
||||
public static class DummyRunnable implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
/* no-op */
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CollectionArgumentConfiguration {
|
||||
|
||||
List<TestBean> testBeans;
|
||||
|
||||
@Bean(autowireCandidate = false)
|
||||
public TestBean thing(List<TestBean> testBeans) {
|
||||
this.testBeans = testBeans;
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class MapArgumentConfiguration {
|
||||
|
||||
Map<String, Runnable> testBeans;
|
||||
|
||||
@Bean(autowireCandidate = false)
|
||||
Runnable testBean(Map<String, Runnable> testBeans) {
|
||||
this.testBeans = testBeans;
|
||||
return () -> {};
|
||||
}
|
||||
|
||||
// Unrelated, not to be considered as a factory method
|
||||
private boolean testBean(boolean param) {
|
||||
return param;
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CollectionInjectionConfiguration {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user