/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.context.annotation;
import java.util.List;
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.*;
/**
* Tests regarding overloading and overriding of bean methods.
* Related to SPR-6618.
*
* @author Chris Beams
* @author Phillip Webb
* @author Juergen Hoeller
*/
@SuppressWarnings("resource")
public class BeanMethodPolymorphismTests {
@Test
public void beanMethodDetectedOnSuperClass() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
ctx.getBean("testBean", TestBean.class);
}
@Test
public void beanMethodOverriding() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(OverridingConfig.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 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();
ctx.register(ConfigWithOverloading.class);
ctx.setAllowBeanDefinitionOverriding(false);
ctx.refresh();
assertThat(ctx.getBean(String.class), equalTo("regular"));
}
@Test
public void beanMethodOverloadingWithoutInheritanceAndExtraDependency() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(ConfigWithOverloading.class);
ctx.getDefaultListableBeanFactory().registerSingleton("anInt", 5);
ctx.setAllowBeanDefinitionOverriding(false);
ctx.refresh();
assertThat(ctx.getBean(String.class), equalTo("overloaded5"));
}
@Test
public void beanMethodOverloadingWithAdditionalMetadata() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(ConfigWithOverloadingAndAdditionalMetadata.class);
ctx.setAllowBeanDefinitionOverriding(false);
ctx.refresh();
assertFalse(ctx.getDefaultListableBeanFactory().containsSingleton("aString"));
assertThat(ctx.getBean(String.class), equalTo("regular"));
assertTrue(ctx.getDefaultListableBeanFactory().containsSingleton("aString"));
}
@Test
public void beanMethodOverloadingWithAdditionalMetadataButOtherMethodExecuted() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(ConfigWithOverloadingAndAdditionalMetadata.class);
ctx.getDefaultListableBeanFactory().registerSingleton("anInt", 5);
ctx.setAllowBeanDefinitionOverriding(false);
ctx.refresh();
assertFalse(ctx.getDefaultListableBeanFactory().containsSingleton("aString"));
assertThat(ctx.getBean(String.class), equalTo("overloaded5"));
assertTrue(ctx.getDefaultListableBeanFactory().containsSingleton("aString"));
}
@Test
public void beanMethodOverloadingWithInheritance() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(SubConfig.class);
ctx.setAllowBeanDefinitionOverriding(false);
ctx.refresh();
assertFalse(ctx.getDefaultListableBeanFactory().containsSingleton("aString"));
assertThat(ctx.getBean(String.class), equalTo("overloaded5"));
assertTrue(ctx.getDefaultListableBeanFactory().containsSingleton("aString"));
}
// SPR-11025
@Test
public void beanMethodOverloadingWithInheritanceAndList() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(SubConfigWithList.class);
ctx.setAllowBeanDefinitionOverriding(false);
ctx.refresh();
assertFalse(ctx.getDefaultListableBeanFactory().containsSingleton("aString"));
assertThat(ctx.getBean(String.class), equalTo("overloaded5"));
assertTrue(ctx.getDefaultListableBeanFactory().containsSingleton("aString"));
}
/**
* When inheritance is not involved, it is still possible to override a bean method from
* the container's point of view. This is not strictly 'overloading' of a method per se,
* so it's referred to here as 'shadowing' to distinguish the difference.
*/
@Test
public void beanMethodShadowing() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ShadowConfig.class);
assertThat(ctx.getBean(String.class), equalTo("shadow"));
}
@Test
public void beanMethodThroughAopProxy() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(Config.class);
ctx.register(AnnotationAwareAspectJAutoProxyCreator.class);
ctx.register(TestAdvisor.class);
ctx.refresh();
ctx.getBean("testBean", TestBean.class);
}
@Configuration
static class BaseConfig {
@Bean
public TestBean testBean() {
return new TestBean();
}
}
@Configuration
static class Config extends BaseConfig {
}
@Configuration
static class OverridingConfig extends BaseConfig {
@Bean @Lazy
@Override
public TestBean testBean() {
return new TestBean() {
@Override
public String toString() {
return "overridden";
}
};
}
}
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 {
@Bean
String aString() {
return "regular";
}
@Bean
String aString(Integer dependency) {
return "overloaded" + dependency;
}
}
@Configuration
static class ConfigWithOverloadingAndAdditionalMetadata {
@Bean @Lazy
String aString() {
return "regular";
}
@Bean @Lazy
String aString(Integer dependency) {
return "overloaded" + dependency;
}
}
@Configuration
static class SuperConfig {
@Bean
String aString() {
return "super";
}
}
@Configuration
static class SubConfig extends SuperConfig {
@Bean
Integer anInt() {
return 5;
}
@Bean @Lazy
String aString(Integer dependency) {
return "overloaded" + dependency;
}
}
@Configuration
static class SubConfigWithList extends SuperConfig {
@Bean
Integer anInt() {
return 5;
}
@Bean @Lazy
String aString(List<Integer> dependency) {
return "overloaded" + dependency.get(0);
}
}
@Configuration
@Import(SubConfig.class)
static class ShadowConfig {
@Bean
String aString() {
return "shadow";
}
}
@SuppressWarnings("serial")
public static class TestAdvisor extends DefaultPointcutAdvisor {
public TestAdvisor() {
super(new SimpleTraceInterceptor());
}
}
}