diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java index 698a04d2498..77f5301c486 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 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. @@ -35,6 +35,7 @@ import org.springframework.cglib.proxy.Factory; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import org.springframework.cglib.proxy.NoOp; +import org.springframework.core.ResolvableType; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -244,8 +245,10 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt return (bean.equals(null) ? null : bean); } else { - return (argsToUse != null ? this.owner.getBean(method.getReturnType(), argsToUse) : - this.owner.getBean(method.getReturnType())); + // Find target bean matching the (potentially generic) method return type + ResolvableType genericReturnType = ResolvableType.forMethodReturnType(method); + return (argsToUse != null ? this.owner.getBeanProvider(genericReturnType).getObject(argsToUse) : + this.owner.getBeanProvider(genericReturnType).getObject()); } } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/LookupOverride.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/LookupOverride.java index b424c38bb08..c441bf7e9cc 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/LookupOverride.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/LookupOverride.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2021 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. @@ -19,17 +19,25 @@ package org.springframework.beans.factory.support; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import org.springframework.core.ResolvableType; import org.springframework.lang.Nullable; import org.springframework.util.ObjectUtils; /** - * Represents an override of a method that looks up an object in the same IoC context. + * Represents an override of a method that looks up an object in the same IoC context, + * either by bean name or by bean type (based on the declared method return type). * - *

Methods eligible for lookup override must not have arguments. + *

Methods eligible for lookup override may declare arguments in which case the + * given arguments are passed to the bean retrieval operation. * * @author Rod Johnson * @author Juergen Hoeller * @since 1.1 + * @see org.springframework.beans.factory.BeanFactory#getBean(String) + * @see org.springframework.beans.factory.BeanFactory#getBean(Class) + * @see org.springframework.beans.factory.BeanFactory#getBean(String, Object...) + * @see org.springframework.beans.factory.BeanFactory#getBean(Class, Object...) + * @see org.springframework.beans.factory.BeanFactory#getBeanProvider(ResolvableType) */ public class LookupOverride extends MethodOverride { @@ -43,8 +51,8 @@ public class LookupOverride extends MethodOverride { /** * Construct a new LookupOverride. * @param methodName the name of the method to override - * @param beanName the name of the bean in the current {@code BeanFactory} - * that the overridden method should return (may be {@code null}) + * @param beanName the name of the bean in the current {@code BeanFactory} that the + * overridden method should return (may be {@code null} for type-based bean retrieval) */ public LookupOverride(String methodName, @Nullable String beanName) { super(methodName); @@ -53,9 +61,9 @@ public class LookupOverride extends MethodOverride { /** * Construct a new LookupOverride. - * @param method the method to override - * @param beanName the name of the bean in the current {@code BeanFactory} - * that the overridden method should return (may be {@code null}) + * @param method the method declaration to override + * @param beanName the name of the bean in the current {@code BeanFactory} that the + * overridden method should return (may be {@code null} for type-based bean retrieval) */ public LookupOverride(Method method, @Nullable String beanName) { super(method.getName()); diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/MethodReplacer.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/MethodReplacer.java index 5677d944ff3..e4e5df879e5 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/MethodReplacer.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/MethodReplacer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2021 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. @@ -19,9 +19,8 @@ package org.springframework.beans.factory.support; import java.lang.reflect.Method; /** - * Interface to be implemented by classes that can reimplement any method - * on an IoC-managed object: the Method Injection form of - * Dependency Injection. + * Interface to be implemented by classes that can reimplement any method on an + * IoC-managed object: the Method Injection form of Dependency Injection. * *

Such methods may be (but need not be) abstract, in which case the * container will create a concrete subclass to instantiate. diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/ReplaceOverride.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/ReplaceOverride.java index 1b51bf70655..80f866d21cc 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/ReplaceOverride.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/ReplaceOverride.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 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. @@ -25,7 +25,7 @@ import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; /** - * Extension of MethodOverride that represents an arbitrary + * Extension of {@link MethodOverride} that represents an arbitrary * override of a method by the IoC container. * *

Any non-final method can be overridden, irrespective of its @@ -45,7 +45,7 @@ public class ReplaceOverride extends MethodOverride { /** * Construct a new ReplaceOverride. * @param methodName the name of the method to override - * @param methodReplacerBeanName the bean name of the MethodReplacer + * @param methodReplacerBeanName the bean name of the {@link MethodReplacer} */ public ReplaceOverride(String methodName, String methodReplacerBeanName) { super(methodName); diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/LookupAnnotationTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/LookupAnnotationTests.java index bd30b5b44c2..afa61bd9a60 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/LookupAnnotationTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/LookupAnnotationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 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. @@ -121,6 +121,18 @@ public class LookupAnnotationTests { assertThat(beanFactory.getBean(BeanConsumer.class).abstractBean).isSameAs(bean); } + @Test + public void testWithGenericBean() { + beanFactory.registerBeanDefinition("numberBean", new RootBeanDefinition(NumberBean.class)); + beanFactory.registerBeanDefinition("doubleStore", new RootBeanDefinition(DoubleStore.class)); + beanFactory.registerBeanDefinition("floatStore", new RootBeanDefinition(FloatStore.class)); + + NumberBean bean = (NumberBean) beanFactory.getBean("numberBean"); + assertThat(bean).isNotNull(); + assertThat(beanFactory.getBean(DoubleStore.class)).isSameAs(bean.getDoubleStore()); + assertThat(beanFactory.getBean(FloatStore.class)).isSameAs(bean.getFloatStore()); + } + public static abstract class AbstractBean { @@ -147,4 +159,26 @@ public class LookupAnnotationTests { AbstractBean abstractBean; } + + public static class NumberStore { + } + + + public static class DoubleStore extends NumberStore { + } + + + public static class FloatStore extends NumberStore { + } + + + public static abstract class NumberBean { + + @Lookup + public abstract NumberStore getDoubleStore(); + + @Lookup + public abstract NumberStore getFloatStore(); + } + } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/support/LookupMethodTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/support/LookupMethodTests.java index eeb34b6f8f1..414fd015766 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/support/LookupMethodTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/support/LookupMethodTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 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. @@ -36,7 +36,7 @@ public class LookupMethodTests { @BeforeEach - public void setUp() { + public void setup() { beanFactory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory); reader.loadBeanDefinitions(new ClassPathResource("lookupMethodTests.xml", getClass())); @@ -83,8 +83,8 @@ public class LookupMethodTests { public void testWithThreeArgsShouldFail() { AbstractBean bean = (AbstractBean) beanFactory.getBean("abstractBean"); assertThat(bean).isNotNull(); - assertThatExceptionOfType(AbstractMethodError.class).as("does not have a three arg constructor").isThrownBy(() -> - bean.getThreeArguments("name", 1, 2)); + assertThatExceptionOfType(AbstractMethodError.class).as("does not have a three arg constructor") + .isThrownBy(() -> bean.getThreeArguments("name", 1, 2)); } @Test @@ -97,6 +97,21 @@ public class LookupMethodTests { assertThat(expected.isJedi()).isTrue(); } + @Test + public void testWithGenericBean() { + RootBeanDefinition bd = new RootBeanDefinition(NumberBean.class); + bd.getMethodOverrides().addOverride(new LookupOverride("getDoubleStore", null)); + bd.getMethodOverrides().addOverride(new LookupOverride("getFloatStore", null)); + beanFactory.registerBeanDefinition("numberBean", bd); + beanFactory.registerBeanDefinition("doubleStore", new RootBeanDefinition(DoubleStore.class)); + beanFactory.registerBeanDefinition("floatStore", new RootBeanDefinition(FloatStore.class)); + + NumberBean bean = (NumberBean) beanFactory.getBean("numberBean"); + assertThat(bean).isNotNull(); + assertThat(beanFactory.getBean(DoubleStore.class)).isSameAs(bean.getDoubleStore()); + assertThat(beanFactory.getBean(FloatStore.class)).isSameAs(bean.getFloatStore()); + } + public static abstract class AbstractBean { @@ -111,4 +126,24 @@ public class LookupMethodTests { public abstract TestBean getThreeArguments(String name, int age, int anotherArg); } + + public static class NumberStore { + } + + + public static class DoubleStore extends NumberStore { + } + + + public static class FloatStore extends NumberStore { + } + + + public static abstract class NumberBean { + + public abstract NumberStore getDoubleStore(); + + public abstract NumberStore getFloatStore(); + } + }