@Bean processing explicitly ignores bridge methods (for method overrides with return type narrowing on JDK 8)
Issue: SPR-11718
This commit is contained in:
parent
5d5f70473a
commit
dc934064a2
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -23,6 +23,7 @@ import org.junit.Test;
|
||||||
import org.junit.rules.ExpectedException;
|
import org.junit.rules.ExpectedException;
|
||||||
|
|
||||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||||
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.*;
|
import static org.hamcrest.CoreMatchers.*;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
@ -58,6 +59,39 @@ public class BeanMethodPolymorphismTests {
|
||||||
assertTrue(ctx.getDefaultListableBeanFactory().containsSingleton("testBean"));
|
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
|
@Test
|
||||||
public void beanMethodOverloadingWithoutInheritance() {
|
public void beanMethodOverloadingWithoutInheritance() {
|
||||||
|
|
||||||
|
@ -138,6 +172,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
|
@Configuration
|
||||||
static class SuperConfig {
|
static class SuperConfig {
|
||||||
|
|
||||||
|
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -160,6 +160,7 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
|
||||||
public boolean hasAnnotatedMethods(String annotationType) {
|
public boolean hasAnnotatedMethods(String annotationType) {
|
||||||
Method[] methods = getIntrospectedClass().getDeclaredMethods();
|
Method[] methods = getIntrospectedClass().getDeclaredMethods();
|
||||||
for (Method method : methods) {
|
for (Method method : methods) {
|
||||||
|
if (!method.isBridge()) {
|
||||||
for (Annotation ann : method.getAnnotations()) {
|
for (Annotation ann : method.getAnnotations()) {
|
||||||
if (ann.annotationType().getName().equals(annotationType)) {
|
if (ann.annotationType().getName().equals(annotationType)) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -173,13 +174,15 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<MethodMetadata> getAnnotatedMethods(String annotationType) {
|
public Set<MethodMetadata> getAnnotatedMethods(String annotationType) {
|
||||||
Method[] methods = getIntrospectedClass().getDeclaredMethods();
|
|
||||||
Set<MethodMetadata> annotatedMethods = new LinkedHashSet<MethodMetadata>();
|
Set<MethodMetadata> annotatedMethods = new LinkedHashSet<MethodMetadata>();
|
||||||
|
Method[] methods = getIntrospectedClass().getDeclaredMethods();
|
||||||
for (Method method : methods) {
|
for (Method method : methods) {
|
||||||
|
if (!method.isBridge()) {
|
||||||
for (Annotation ann : method.getAnnotations()) {
|
for (Annotation ann : method.getAnnotations()) {
|
||||||
if (ann.annotationType().getName().equals(annotationType)) {
|
if (ann.annotationType().getName().equals(annotationType)) {
|
||||||
annotatedMethods.add(new StandardMethodMetadata(method, this.nestedAnnotationsAsMap));
|
annotatedMethods.add(new StandardMethodMetadata(method, this.nestedAnnotationsAsMap));
|
||||||
|
@ -195,6 +198,7 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return annotatedMethods;
|
return annotatedMethods;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -19,12 +19,12 @@ package org.springframework.core.type.classreading;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.springframework.asm.AnnotationVisitor;
|
import org.springframework.asm.AnnotationVisitor;
|
||||||
import org.springframework.asm.MethodVisitor;
|
import org.springframework.asm.MethodVisitor;
|
||||||
|
import org.springframework.asm.Opcodes;
|
||||||
import org.springframework.asm.Type;
|
import org.springframework.asm.Type;
|
||||||
import org.springframework.core.annotation.AnnotationAttributes;
|
import org.springframework.core.annotation.AnnotationAttributes;
|
||||||
import org.springframework.core.type.AnnotationMetadata;
|
import org.springframework.core.type.AnnotationMetadata;
|
||||||
|
@ -60,6 +60,11 @@ final class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
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);
|
return new MethodMetadataReadingVisitor(name, access, getClassName(), this.classLoader, this.methodMetadataSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue