From 2c3c3831c179df3bf22edc6bcf6323631a32156e Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 17 Jun 2024 18:42:20 +0200 Subject: [PATCH] Consistently ignore bridge method on generated subclass for visibility purposes Closes gh-33030 --- .../AutowiredConfigurationTests.java | 36 +++++++++++++++++++ .../core/BridgeMethodResolver.java | 5 +++ 2 files changed, 41 insertions(+) diff --git a/spring-context/src/test/java/org/springframework/context/annotation/configuration/AutowiredConfigurationTests.java b/spring-context/src/test/java/org/springframework/context/annotation/configuration/AutowiredConfigurationTests.java index 9b9a6e5e374..00cd00e3b8c 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/configuration/AutowiredConfigurationTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/configuration/AutowiredConfigurationTests.java @@ -26,6 +26,7 @@ import jakarta.inject.Provider; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -43,6 +44,7 @@ import org.springframework.context.support.GenericApplicationContext; import org.springframework.core.annotation.AliasFor; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; +import org.springframework.util.Assert; import static org.assertj.core.api.Assertions.assertThat; @@ -183,6 +185,14 @@ class AutowiredConfigurationTests { context.close(); } + @Test + void testValueInjectionWithAccidentalAutowiredAnnotations() { + AnnotationConfigApplicationContext context = + new AnnotationConfigApplicationContext(ValueConfigWithAccidentalAutowiredAnnotations.class); + doTestValueInjection(context); + context.close(); + } + private void doTestValueInjection(BeanFactory context) { System.clearProperty("myProp"); @@ -494,6 +504,32 @@ class AutowiredConfigurationTests { } + @Configuration + static class ValueConfigWithAccidentalAutowiredAnnotations implements InitializingBean { + + boolean invoked; + + @Override + public void afterPropertiesSet() { + Assert.state(!invoked, "Factory method must not get invoked on startup"); + } + + @Bean @Scope("prototype") + @Autowired + public TestBean testBean(@Value("#{systemProperties[myProp]}") Provider name) { + invoked = true; + return new TestBean(name.get()); + } + + @Bean @Scope("prototype") + @Autowired + public TestBean testBean2(@Value("#{systemProperties[myProp]}") Provider name2) { + invoked = true; + return new TestBean(name2.get()); + } + } + + @Configuration static class PropertiesConfig { diff --git a/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java b/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java index 10388c522d5..a5c66620251 100644 --- a/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java +++ b/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java @@ -276,8 +276,13 @@ public final class BridgeMethodResolver { */ public static boolean isVisibilityBridgeMethodPair(Method bridgeMethod, Method bridgedMethod) { if (bridgeMethod == bridgedMethod) { + // Same method: for common purposes, return true to proceed as if it was a visibility bridge. return true; } + if (ClassUtils.getUserClass(bridgeMethod.getDeclaringClass()) != bridgeMethod.getDeclaringClass()) { + // Method on generated subclass: return false to consistently ignore it for visibility purposes. + return false; + } return (bridgeMethod.getReturnType().equals(bridgedMethod.getReturnType()) && bridgeMethod.getParameterCount() == bridgedMethod.getParameterCount() && Arrays.equals(bridgeMethod.getParameterTypes(), bridgedMethod.getParameterTypes()));