Skip to content

Commit 310bdbc

Browse files
committed
@bean processing explicitly ignores bridge methods (for method overrides with return type narrowing on JDK 8)
Issue: SPR-11718 (cherry picked from commit 656fc52)
1 parent 1f630a5 commit 310bdbc

File tree

3 files changed

+64
-5
lines changed

3 files changed

+64
-5
lines changed

spring-context/src/test/java/org/springframework/context/annotation/BeanMethodPolymorphismTests.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
2424
import org.springframework.aop.interceptor.SimpleTraceInterceptor;
2525
import org.springframework.aop.support.DefaultPointcutAdvisor;
26+
import org.springframework.beans.factory.support.RootBeanDefinition;
2627

2728
import static org.hamcrest.CoreMatchers.*;
2829
import static org.junit.Assert.*;
@@ -55,6 +56,39 @@ public void beanMethodOverriding() {
5556
assertTrue(ctx.getDefaultListableBeanFactory().containsSingleton("testBean"));
5657
}
5758

59+
@Test
60+
public void beanMethodOverridingOnASM() {
61+
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
62+
ctx.registerBeanDefinition("config", new RootBeanDefinition(OverridingConfig.class.getName()));
63+
ctx.setAllowBeanDefinitionOverriding(false);
64+
ctx.refresh();
65+
assertFalse(ctx.getDefaultListableBeanFactory().containsSingleton("testBean"));
66+
assertEquals("overridden", ctx.getBean("testBean", TestBean.class).toString());
67+
assertTrue(ctx.getDefaultListableBeanFactory().containsSingleton("testBean"));
68+
}
69+
70+
@Test
71+
public void beanMethodOverridingWithNarrowedReturnType() {
72+
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
73+
ctx.register(NarrowedOverridingConfig.class);
74+
ctx.setAllowBeanDefinitionOverriding(false);
75+
ctx.refresh();
76+
assertFalse(ctx.getDefaultListableBeanFactory().containsSingleton("testBean"));
77+
assertEquals("overridden", ctx.getBean("testBean", TestBean.class).toString());
78+
assertTrue(ctx.getDefaultListableBeanFactory().containsSingleton("testBean"));
79+
}
80+
81+
@Test
82+
public void beanMethodOverridingWithNarrowedReturnTypeOnASM() {
83+
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
84+
ctx.registerBeanDefinition("config", new RootBeanDefinition(NarrowedOverridingConfig.class.getName()));
85+
ctx.setAllowBeanDefinitionOverriding(false);
86+
ctx.refresh();
87+
assertFalse(ctx.getDefaultListableBeanFactory().containsSingleton("testBean"));
88+
assertEquals("overridden", ctx.getBean("testBean", TestBean.class).toString());
89+
assertTrue(ctx.getDefaultListableBeanFactory().containsSingleton("testBean"));
90+
}
91+
5892
@Test
5993
public void beanMethodOverloadingWithoutInheritance() {
6094
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
@@ -173,6 +207,26 @@ public String toString() {
173207
}
174208

175209

210+
static class ExtendedTestBean extends TestBean {
211+
}
212+
213+
214+
@Configuration
215+
static class NarrowedOverridingConfig extends BaseConfig {
216+
217+
@Bean @Lazy
218+
@Override
219+
public ExtendedTestBean testBean() {
220+
return new ExtendedTestBean() {
221+
@Override
222+
public String toString() {
223+
return "overridden";
224+
}
225+
};
226+
}
227+
}
228+
229+
176230
@Configuration
177231
static class ConfigWithOverloading {
178232

spring-core/src/main/java/org/springframework/core/type/StandardAnnotationMetadata.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -128,7 +128,7 @@ public MultiValueMap<String, Object> getAllAnnotationAttributes(String annotatio
128128
public boolean hasAnnotatedMethods(String annotationType) {
129129
Method[] methods = getIntrospectedClass().getDeclaredMethods();
130130
for (Method method : methods) {
131-
if (AnnotatedElementUtils.isAnnotated(method, annotationType)) {
131+
if (!method.isBridge() && AnnotatedElementUtils.isAnnotated(method, annotationType)) {
132132
return true;
133133
}
134134
}
@@ -140,7 +140,7 @@ public Set<MethodMetadata> getAnnotatedMethods(String annotationType) {
140140
Method[] methods = getIntrospectedClass().getDeclaredMethods();
141141
Set<MethodMetadata> annotatedMethods = new LinkedHashSet<MethodMetadata>();
142142
for (Method method : methods) {
143-
if (AnnotatedElementUtils.isAnnotated(method, annotationType)) {
143+
if (!method.isBridge() && AnnotatedElementUtils.isAnnotated(method, annotationType)) {
144144
annotatedMethods.add(new StandardMethodMetadata(method, this.nestedAnnotationsAsMap));
145145
}
146146
}

spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationMetadataReadingVisitor.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
import org.springframework.asm.AnnotationVisitor;
2727
import org.springframework.asm.MethodVisitor;
28+
import org.springframework.asm.Opcodes;
2829
import org.springframework.asm.Type;
2930
import org.springframework.core.annotation.AnnotationAttributes;
3031
import org.springframework.core.type.AnnotationMetadata;
@@ -57,8 +58,7 @@ public class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisito
5758
* to ensure that the hierarchical ordering of the entries is preserved.
5859
* @see AnnotationReadingVisitorUtils#getMergedAnnotationAttributes(LinkedMultiValueMap, String)
5960
*/
60-
protected final LinkedMultiValueMap<String, AnnotationAttributes> attributesMap = new LinkedMultiValueMap<String, AnnotationAttributes>(
61-
4);
61+
protected final LinkedMultiValueMap<String, AnnotationAttributes> attributesMap = new LinkedMultiValueMap<String, AnnotationAttributes>(4);
6262

6363
protected final Set<MethodMetadata> methodMetadataSet = new LinkedHashSet<MethodMetadata>(4);
6464

@@ -70,6 +70,11 @@ public AnnotationMetadataReadingVisitor(ClassLoader classLoader) {
7070

7171
@Override
7272
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
73+
// Skip bridge methods - we're only interested in original annotation-defining user methods.
74+
// On JDK 8, we'd otherwise run into double detection of the same annotated method...
75+
if ((access & Opcodes.ACC_BRIDGE) != 0) {
76+
return super.visitMethod(access, name, desc, signature, exceptions);
77+
}
7378
return new MethodMetadataReadingVisitor(name, access, getClassName(), this.classLoader, this.methodMetadataSet);
7479
}
7580

0 commit comments

Comments
 (0)