Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
* @author Rod Johnson
* @author Juergen Hoeller
* @author Chris Beams
* @author Yanming Zhou
* @since 13 April 2001
* @see BeanNameAware#setBeanName
* @see BeanClassLoaderAware#setBeanClassLoader
Expand Down Expand Up @@ -175,6 +176,29 @@ public interface BeanFactory {
*/
<T> T getBean(String name, Class<T> requiredType) throws BeansException;

/**
* Return an instance, which may be shared or independent, of the specified bean.
* <p>Behaves the same as {@link #getBean(String)}, but provides a measure of type
* safety by throwing a BeanNotOfRequiredTypeException if the bean is not of the
* required type. This means that ClassCastException can't be thrown on casting
* the result correctly, as can happen with {@link #getBean(String)}.
* <p>Translates aliases back to the corresponding canonical bean name.
* <p>Will ask the parent factory if the bean cannot be found in this factory instance.
* @param name the name of the bean to retrieve
* @param typeReference the reference to obtain type the bean must match
* @return an instance of the bean.
* Note that the return value will never be {@code null}. In case of a stub for
* {@code null} from a factory method having been resolved for the requested bean, a
* {@code BeanNotOfRequiredTypeException} against the NullBean stub will be raised.
* Consider using {@link #getBeanProvider(Class)} for resolving optional dependencies.
* @throws NoSuchBeanDefinitionException if there is no such bean definition
* @throws BeanNotOfRequiredTypeException if the bean is not of the required type
* @throws BeansException if the bean could not be created
* @since 7.1
* @see #getBean(String, Class)
*/
<T> T getBean(String name, ParameterizedTypeReference<T> typeReference) throws BeansException;

/**
* Return an instance, which may be shared or independent, of the specified bean.
* <p>Allows for specifying explicit constructor arguments / factory method arguments,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,18 @@

package org.springframework.beans.factory;

import java.lang.reflect.Type;

import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;
import org.springframework.util.ClassUtils;

/**
* Thrown when a bean doesn't match the expected type.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Yanming Zhou
*/
@SuppressWarnings("serial")
public class BeanNotOfRequiredTypeException extends BeansException {
Expand All @@ -32,7 +36,7 @@ public class BeanNotOfRequiredTypeException extends BeansException {
private final String beanName;

/** The required type. */
private final Class<?> requiredType;
private final Type genericRequiredType;

/** The offending type. */
private final Class<?> actualType;
Expand All @@ -46,10 +50,22 @@ public class BeanNotOfRequiredTypeException extends BeansException {
* the expected type
*/
public BeanNotOfRequiredTypeException(String beanName, Class<?> requiredType, Class<?> actualType) {
super("Bean named '" + beanName + "' is expected to be of type '" + ClassUtils.getQualifiedName(requiredType) +
this(beanName, (Type) requiredType, actualType);
}

/**
* Create a new BeanNotOfRequiredTypeException.
* @param beanName the name of the bean requested
* @param requiredType the required type
* @param actualType the actual type returned, which did not match
* the expected type
* @since 7.1
*/
public BeanNotOfRequiredTypeException(String beanName, Type requiredType, Class<?> actualType) {
super("Bean named '" + beanName + "' is expected to be of type '" + requiredType.getTypeName() +
"' but was actually of type '" + ClassUtils.getQualifiedName(actualType) + "'");
this.beanName = beanName;
this.requiredType = requiredType;
this.genericRequiredType = requiredType;
this.actualType = actualType;
}

Expand All @@ -65,7 +81,15 @@ public String getBeanName() {
* Return the expected type for the bean.
*/
public Class<?> getRequiredType() {
return this.requiredType;
return (this.genericRequiredType instanceof Class<?> clazz ? clazz : ResolvableType.forType(this.genericRequiredType).toClass());
}

/**
* Return the expected generic type for the bean.
* @since 7.1
*/
public Type getGenericRequiredType() {
return this.genericRequiredType;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.springframework.beans.factory.support;

import java.beans.PropertyEditor;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
Expand Down Expand Up @@ -66,6 +67,7 @@
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
import org.springframework.core.DecoratingClassLoader;
import org.springframework.core.NamedThreadLocal;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.log.LogMessage;
Expand Down Expand Up @@ -201,6 +203,17 @@ public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
return doGetBean(name, requiredType, null, false);
}

@Override
@SuppressWarnings("unchecked")
public <T> T getBean(String name, ParameterizedTypeReference<T> typeReference) throws BeansException {
Object bean = getBean(name);
Type requiredType = typeReference.getType();
if (!ResolvableType.forType(requiredType).isInstance(bean)) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return (T) bean;
}

@Override
public Object getBean(String name, @Nullable Object @Nullable ... args) throws BeansException {
return doGetBean(name, null, args, false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.springframework.beans.factory.support;

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
Expand Down Expand Up @@ -64,6 +65,7 @@
* @author Rod Johnson
* @author Juergen Hoeller
* @author Sam Brannen
* @author Yanming Zhou
* @since 06.01.2003
* @see DefaultListableBeanFactory
*/
Expand Down Expand Up @@ -149,6 +151,17 @@ else if (bean instanceof FactoryBean<?> factoryBean) {
return (T) bean;
}

@Override
@SuppressWarnings("unchecked")
public <T> T getBean(String name, ParameterizedTypeReference<T> typeReference) throws BeansException {
Object bean = getBean(name);
Type requiredType = typeReference.getType();
if (!ResolvableType.forType(requiredType).isInstance(bean)) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return (T) bean;
}

@Override
public Object getBean(String name, @Nullable Object @Nullable ... args) throws BeansException {
if (!ObjectUtils.isEmpty(args)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,22 @@ import org.springframework.core.ResolvableType
* This extension is not subject to type erasure and retains actual generic type arguments.
*
* @author Sebastien Deleuze
* @author Yanming Zhou
* @since 5.0
*/
inline fun <reified T : Any> BeanFactory.getBean(): T =
getBeanProvider<T>().getObject()

/**
* Extension for [BeanFactory.getBean] providing a `getBean<Foo>("foo")` variant.
* Like the original Java method, this extension is subject to type erasure.
* This extension is not subject to type erasure and retains actual generic type arguments.
*
* @see BeanFactory.getBean(String, Class<T>)
* @author Sebastien Deleuze
* @since 5.0
*/
inline fun <reified T : Any> BeanFactory.getBean(name: String): T =
getBean(name, T::class.java)
getBean(name, (object : ParameterizedTypeReference<T>() {}))

/**
* Extension for [BeanFactory.getBean] providing a `getBean<Foo>(arg1, arg2)` variant.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
import org.springframework.beans.testfixture.beans.factory.DummyFactory;
import org.springframework.core.MethodParameter;
import org.springframework.core.Ordered;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.annotation.Order;
Expand Down Expand Up @@ -1682,6 +1683,29 @@ void getBeanByTypeWithAmbiguity() {
lbf.getBean(TestBean.class));
}

@Test
void getBeanByNameWithTypeReference() {
RootBeanDefinition bd1 = new RootBeanDefinition(StringTemplate.class);
RootBeanDefinition bd2 = new RootBeanDefinition(NumberTemplate.class);
lbf.registerBeanDefinition("bd1", bd1);
lbf.registerBeanDefinition("bd2", bd2);

Template<String> stringTemplate = lbf.getBean("bd1", new ParameterizedTypeReference<>() {});
Template<Number> numberTemplate = lbf.getBean("bd2", new ParameterizedTypeReference<>() {});

assertThat(stringTemplate).isInstanceOf(StringTemplate.class);
assertThat(numberTemplate).isInstanceOf(NumberTemplate.class);

assertThatExceptionOfType(BeanNotOfRequiredTypeException.class)
.isThrownBy(() -> lbf.getBean("bd2", new ParameterizedTypeReference<Template<String>>() {}))
.satisfies(ex -> {
assertThat(ex.getBeanName()).isEqualTo("bd2");
assertThat(ex.getRequiredType()).isEqualTo(Template.class);
assertThat(ex.getActualType()).isEqualTo(NumberTemplate.class);
assertThat(ex.getGenericRequiredType().toString()).endsWith("Template<java.lang.String>");
});
}

@Test
void getBeanByTypeWithPrimary() {
RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class);
Expand Down Expand Up @@ -3872,4 +3896,16 @@ public Class<?> getObjectType() {
}
}

private static class Template<T> {

}

private static class StringTemplate extends Template<String> {

}

private static class NumberTemplate extends Template<Number> {

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import io.mockk.mockk
import io.mockk.verify
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.core.ParameterizedTypeReference
import org.springframework.core.ResolvableType

/**
Expand Down Expand Up @@ -53,7 +54,16 @@ class BeanFactoryExtensionsTests {
fun `getBean with String and reified type parameters`() {
val name = "foo"
bf.getBean<Foo>(name)
verify { bf.getBean(name, Foo::class.java) }
verify { bf.getBean(name, ofType<ParameterizedTypeReference<Foo>>()) }
}

@Test
fun `getBean with String and reified generic type parameters`() {
val name = "foo"
val foo = listOf(Foo())
every { bf.getBean(name, ofType<ParameterizedTypeReference<List<Foo>>>()) } returns foo
assertThat(bf.getBean<List<Foo>>("foo")).isSameAs(foo)
verify { bf.getBean(name, ofType<ParameterizedTypeReference<List<Foo>>>()) }
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@
* @author Sam Brannen
* @author Sebastien Deleuze
* @author Brian Clozel
* @author Yanming Zhou
* @since January 21, 2001
* @see #refreshBeanFactory
* @see #getBeanFactory
Expand Down Expand Up @@ -1305,6 +1306,12 @@ public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
return getBeanFactory().getBean(name, requiredType);
}

@Override
public <T> T getBean(String name, ParameterizedTypeReference<T> typeReference) throws BeansException {
assertBeanFactoryActive();
return getBeanFactory().getBean(name, typeReference);
}

@Override
public Object getBean(String name, @Nullable Object @Nullable ... args) throws BeansException {
assertBeanFactoryActive();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.springframework.jndi.support;

import java.lang.reflect.Type;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
Expand Down Expand Up @@ -59,6 +60,7 @@
* in particular if BeanFactory-style type checking is required.
*
* @author Juergen Hoeller
* @author Yanming Zhou
* @since 2.5
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory
* @see org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
Expand Down Expand Up @@ -132,6 +134,17 @@ public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
}
}

@Override
@SuppressWarnings("unchecked")
public <T> T getBean(String name, ParameterizedTypeReference<T> typeReference) throws BeansException {
Object bean = getBean(name);
Type requiredType = typeReference.getType();
if (!ResolvableType.forType(requiredType).isInstance(bean)) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return (T) bean;
}

@Override
public Object getBean(String name, @Nullable Object @Nullable ... args) throws BeansException {
if (args != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
*
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @author Yanming Zhou
* @since 3.2
*/
class StubWebApplicationContext implements WebApplicationContext {
Expand Down Expand Up @@ -168,6 +169,11 @@ public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
return this.beanFactory.getBean(name, requiredType);
}

@Override
public <T> T getBean(String name, ParameterizedTypeReference<T> typeReference) throws BeansException {
return this.beanFactory.getBean(name, typeReference);
}

@Override
public Object getBean(String name, @Nullable Object @Nullable ... args) throws BeansException {
return this.beanFactory.getBean(name, args);
Expand Down