forked from spring-projects/spring-framework
-
Notifications
You must be signed in to change notification settings - Fork 0
/
BeanMethodPolymorphismTests.java
150 lines (132 loc) · 5.44 KB
/
BeanMethodPolymorphismTests.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/*
* Copyright 2002-2013 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 static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.lang.annotation.Inherited;
import org.junit.Test;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
/**
* Tests regarding overloading and overriding of bean methods.
* Related to SPR-6618.
*
* Bean-annotated methods should be able to be overridden, just as any regular
* method. This is straightforward.
*
* Bean-annotated methods should be able to be overloaded, though supporting this
* is more subtle. Essentially, it must be unambiguous to the container which bean
* method to call. A simple way to think about this is that no one Configuration
* class may declare two bean methods with the same name. In the case of inheritance,
* the most specific subclass bean method will always be the one that is invoked.
*
* @author Chris Beams
*/
public class BeanMethodPolymorphismTests {
@Test
public void beanMethodOverloadingWithoutInheritance() {
@SuppressWarnings({ "hiding" })
@Configuration class Config {
@Bean String aString() { return "na"; }
@Bean String aString(Integer dependency) { return "na"; }
}
try {
new AnnotationConfigApplicationContext(Config.class);
fail("expected bean method overloading exception");
} catch (BeanDefinitionParsingException ex) {
assertTrue(ex.getMessage(), ex.getMessage().contains("2 overloaded @Bean methods named 'aString'"));
}
}
// Pass only because overloaded aString is greedy and has a best match for argument.
@Test
public void beanMethodOverloadingWithInheritance() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SubConfig.class);
assertThat(ctx.getBean(String.class), equalTo("overloaded5"));
}
static @Configuration class SuperConfig {
@Bean String aString() { return "super"; }
}
static @Configuration class SubConfig extends SuperConfig {
@Bean Integer anInt() { return 5; }
@Bean String aString(Integer dependency) { return "overloaded"+dependency; }
}
// Fail when losing greedy overload
@Test
public void beanMethodOverloadingNotGreedyWithInheritance() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(NotGreedySubConfig.class);
assertThat(ctx.getBean(String.class), equalTo("overloaded"));
}
static @Configuration class NotGreedySuperConfig {
@Bean Integer anInt() { return 5; }
@Bean String aString(Integer dependency) { return "super"; }
}
static @Configuration class NotGreedySubConfig extends NotGreedySuperConfig {
@Bean String aString() { return "overloaded"; }
}
// Fail when losing the best match type arguments
@Test
public void beanMethodOverloadingNotBestMathcWithInheritance() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(NotBestMatchSubConfig.class);
assertThat(ctx.getBean(String.class), equalTo("overloaded5"));
}
static @Configuration class NotBestMathcSuperConfig {
@Bean String aString(Integer dependency) { return "super"; }
}
static @Configuration class NotBestMatchSubConfig extends NotBestMathcSuperConfig {
@Bean Integer anInt() { return 5; }
@Bean String aString(Number dependency) { return "overloaded"+dependency; }
}
/**
* 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"));
}
@Import(SubConfig.class)
static @Configuration class ShadowConfig {
@Bean String aString() { return "shadow"; }
}
/**
* Tests that polymorphic Configuration classes need not explicitly redeclare the
* {@link Configuration} annotation. This respects the {@link Inherited} nature
* of the Configuration annotation, even though it's being detected via ASM.
*/
@Test
public void beanMethodsDetectedOnSuperClass() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(Config.class));
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.postProcessBeanFactory(beanFactory);
beanFactory.getBean("testBean", TestBean.class);
}
@Configuration
static class BaseConfig {
@Bean
public TestBean testBean() {
return new TestBean();
}
}
@Configuration
static class Config extends BaseConfig {
}
}