diff --git a/spring-context/src/test/java/org/springframework/context/annotation/BeanMethodPolymorphismTests.java b/spring-context/src/test/java/org/springframework/context/annotation/BeanMethodPolymorphismTests.java index fa8f811146..324b4af1f1 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/BeanMethodPolymorphismTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/BeanMethodPolymorphismTests.java @@ -23,6 +23,7 @@ import org.junit.Test; import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator; import org.springframework.aop.interceptor.SimpleTraceInterceptor; import org.springframework.aop.support.DefaultPointcutAdvisor; +import org.springframework.beans.factory.support.RootBeanDefinition; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; @@ -55,6 +56,39 @@ public class BeanMethodPolymorphismTests { assertTrue(ctx.getDefaultListableBeanFactory().containsSingleton("testBean")); } + @Test + public void beanMethodOverridingOnASM() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.registerBeanDefinition("config", new RootBeanDefinition(OverridingConfig.class.getName())); + ctx.setAllowBeanDefinitionOverriding(false); + ctx.refresh(); + assertFalse(ctx.getDefaultListableBeanFactory().containsSingleton("testBean")); + assertEquals("overridden", ctx.getBean("testBean", TestBean.class).toString()); + assertTrue(ctx.getDefaultListableBeanFactory().containsSingleton("testBean")); + } + + @Test + public void beanMethodOverridingWithNarrowedReturnType() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(NarrowedOverridingConfig.class); + ctx.setAllowBeanDefinitionOverriding(false); + ctx.refresh(); + assertFalse(ctx.getDefaultListableBeanFactory().containsSingleton("testBean")); + assertEquals("overridden", ctx.getBean("testBean", TestBean.class).toString()); + assertTrue(ctx.getDefaultListableBeanFactory().containsSingleton("testBean")); + } + + @Test + public void beanMethodOverridingWithNarrowedReturnTypeOnASM() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.registerBeanDefinition("config", new RootBeanDefinition(NarrowedOverridingConfig.class.getName())); + ctx.setAllowBeanDefinitionOverriding(false); + ctx.refresh(); + assertFalse(ctx.getDefaultListableBeanFactory().containsSingleton("testBean")); + assertEquals("overridden", ctx.getBean("testBean", TestBean.class).toString()); + assertTrue(ctx.getDefaultListableBeanFactory().containsSingleton("testBean")); + } + @Test public void beanMethodOverloadingWithoutInheritance() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); @@ -173,6 +207,26 @@ public class BeanMethodPolymorphismTests { } + static class ExtendedTestBean extends TestBean { + } + + + @Configuration + static class NarrowedOverridingConfig extends BaseConfig { + + @Bean @Lazy + @Override + public ExtendedTestBean testBean() { + return new ExtendedTestBean() { + @Override + public String toString() { + return "overridden"; + } + }; + } + } + + @Configuration static class ConfigWithOverloading { diff --git a/spring-core/src/main/java/org/springframework/core/type/StandardAnnotationMetadata.java b/spring-core/src/main/java/org/springframework/core/type/StandardAnnotationMetadata.java index 538453ee38..cc2a86ce09 100644 --- a/spring-core/src/main/java/org/springframework/core/type/StandardAnnotationMetadata.java +++ b/spring-core/src/main/java/org/springframework/core/type/StandardAnnotationMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -128,7 +128,7 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements public boolean hasAnnotatedMethods(String annotationType) { Method[] methods = getIntrospectedClass().getDeclaredMethods(); for (Method method : methods) { - if (AnnotatedElementUtils.isAnnotated(method, annotationType)) { + if (!method.isBridge() && AnnotatedElementUtils.isAnnotated(method, annotationType)) { return true; } } @@ -140,7 +140,7 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements Method[] methods = getIntrospectedClass().getDeclaredMethods(); Set annotatedMethods = new LinkedHashSet(); for (Method method : methods) { - if (AnnotatedElementUtils.isAnnotated(method, annotationType)) { + if (!method.isBridge() && AnnotatedElementUtils.isAnnotated(method, annotationType)) { annotatedMethods.add(new StandardMethodMetadata(method, this.nestedAnnotationsAsMap)); } } diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationMetadataReadingVisitor.java b/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationMetadataReadingVisitor.java index ca0c71ebbc..a0e2bc6ca8 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationMetadataReadingVisitor.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationMetadataReadingVisitor.java @@ -25,6 +25,7 @@ import java.util.Set; import org.springframework.asm.AnnotationVisitor; import org.springframework.asm.MethodVisitor; +import org.springframework.asm.Opcodes; import org.springframework.asm.Type; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.type.AnnotationMetadata; @@ -57,8 +58,7 @@ public class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisito * to ensure that the hierarchical ordering of the entries is preserved. * @see AnnotationReadingVisitorUtils#getMergedAnnotationAttributes(LinkedMultiValueMap, String) */ - protected final LinkedMultiValueMap attributesMap = new LinkedMultiValueMap( - 4); + protected final LinkedMultiValueMap attributesMap = new LinkedMultiValueMap(4); protected final Set methodMetadataSet = new LinkedHashSet(4); @@ -70,6 +70,11 @@ public class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisito @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + // Skip bridge methods - we're only interested in original annotation-defining user methods. + // On JDK 8, we'd otherwise run into double detection of the same annotated method... + if ((access & Opcodes.ACC_BRIDGE) != 0) { + return super.visitMethod(access, name, desc, signature, exceptions); + } return new MethodMetadataReadingVisitor(name, access, getClassName(), this.classLoader, this.methodMetadataSet); }