View Javadoc
1   /*
2    * Copyright 2002-2014 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.context.annotation.configuration;
18  
19  import java.util.Arrays;
20  import java.util.Collections;
21  import java.util.List;
22  import java.util.Set;
23  import javax.inject.Provider;
24  
25  import org.junit.Test;
26  
27  import org.springframework.beans.factory.BeanClassLoaderAware;
28  import org.springframework.beans.factory.BeanFactory;
29  import org.springframework.beans.factory.FactoryBean;
30  import org.springframework.beans.factory.ListableBeanFactory;
31  import org.springframework.beans.factory.NoSuchBeanDefinitionException;
32  import org.springframework.beans.factory.annotation.Required;
33  import org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor;
34  import org.springframework.beans.factory.annotation.Value;
35  import org.springframework.beans.factory.config.BeanDefinition;
36  import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
37  import org.springframework.beans.factory.config.BeanPostProcessor;
38  import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
39  import org.springframework.beans.factory.config.ListFactoryBean;
40  import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
41  import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
42  import org.springframework.beans.factory.support.DefaultListableBeanFactory;
43  import org.springframework.beans.factory.support.RootBeanDefinition;
44  import org.springframework.context.ApplicationListener;
45  import org.springframework.context.annotation.AnnotationConfigApplicationContext;
46  import org.springframework.context.annotation.AnnotationConfigUtils;
47  import org.springframework.context.annotation.Bean;
48  import org.springframework.context.annotation.Configuration;
49  import org.springframework.context.annotation.ConfigurationClassPostProcessor;
50  import org.springframework.context.annotation.Scope;
51  import org.springframework.context.event.ContextRefreshedEvent;
52  import org.springframework.context.support.GenericApplicationContext;
53  import org.springframework.tests.sample.beans.ITestBean;
54  import org.springframework.tests.sample.beans.TestBean;
55  
56  import static org.junit.Assert.*;
57  
58  /**
59   * Miscellaneous system tests covering {@link Bean} naming, aliases, scoping and error
60   * handling within {@link Configuration} class definitions.
61   *
62   * @author Chris Beams
63   * @author Juergen Hoeller
64   */
65  public class ConfigurationClassProcessingTests {
66  
67  	/**
68  	 * Creates a new {@link BeanFactory}, populates it with a {@link BeanDefinition} for
69  	 * each of the given {@link Configuration} <var>configClasses</var>, and then
70  	 * post-processes the factory using JavaConfig's {@link ConfigurationClassPostProcessor}.
71  	 * When complete, the factory is ready to service requests for any {@link Bean} methods
72  	 * declared by <var>configClasses</var>.
73  	 */
74  	private ListableBeanFactory initBeanFactory(Class<?>... configClasses) {
75  		DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
76  		for (Class<?> configClass : configClasses) {
77  			String configBeanName = configClass.getName();
78  			factory.registerBeanDefinition(configBeanName, new RootBeanDefinition(configClass));
79  		}
80  		ConfigurationClassPostProcessor ccpp = new ConfigurationClassPostProcessor();
81  		ccpp.postProcessBeanDefinitionRegistry(factory);
82  		ccpp.postProcessBeanFactory(factory);
83  		RequiredAnnotationBeanPostProcessor rapp = new RequiredAnnotationBeanPostProcessor();
84  		rapp.setBeanFactory(factory);
85  		factory.addBeanPostProcessor(rapp);
86  		factory.freezeConfiguration();
87  		return factory;
88  	}
89  
90  
91  	@Test
92  	public void customBeanNameIsRespected() {
93  		GenericApplicationContext ac = new GenericApplicationContext();
94  		AnnotationConfigUtils.registerAnnotationConfigProcessors(ac);
95  		ac.registerBeanDefinition("config", new RootBeanDefinition(ConfigWithBeanWithCustomName.class));
96  		ac.refresh();
97  		assertSame(ac.getBean("customName"), ConfigWithBeanWithCustomName.testBean);
98  
99  		// method name should not be registered
100 		try {
101 			ac.getBean("methodName");
102 			fail("bean should not have been registered with 'methodName'");
103 		}
104 		catch (NoSuchBeanDefinitionException ex) {
105 			// expected
106 		}
107 	}
108 
109 	@Test
110 	public void aliasesAreRespected() {
111 		BeanFactory factory = initBeanFactory(ConfigWithBeanWithAliases.class);
112 		assertSame(factory.getBean("name1"), ConfigWithBeanWithAliases.testBean);
113 		String[] aliases = factory.getAliases("name1");
114 		for(String alias : aliases)
115 			assertSame(factory.getBean(alias), ConfigWithBeanWithAliases.testBean);
116 
117 		// method name should not be registered
118 		try {
119 			factory.getBean("methodName");
120 			fail("bean should not have been registered with 'methodName'");
121 		}
122 		catch (NoSuchBeanDefinitionException ex) {
123 			// expected
124 		}
125 	}
126 
127 	@Test  // SPR-11830
128 	public void configWithBeanWithProviderImplementation() {
129 		GenericApplicationContext ac = new GenericApplicationContext();
130 		AnnotationConfigUtils.registerAnnotationConfigProcessors(ac);
131 		ac.registerBeanDefinition("config", new RootBeanDefinition(ConfigWithBeanWithProviderImplementation.class));
132 		ac.refresh();
133 		assertSame(ac.getBean("customName"), ConfigWithBeanWithProviderImplementation.testBean);
134 	}
135 
136 	@Test  // SPR-11830
137 	public void configWithSetWithProviderImplementation() {
138 		GenericApplicationContext ac = new GenericApplicationContext();
139 		AnnotationConfigUtils.registerAnnotationConfigProcessors(ac);
140 		ac.registerBeanDefinition("config", new RootBeanDefinition(ConfigWithSetWithProviderImplementation.class));
141 		ac.refresh();
142 		assertSame(ac.getBean("customName"), ConfigWithSetWithProviderImplementation.set);
143 	}
144 
145 	@Test(expected=BeanDefinitionParsingException.class)
146 	public void testFinalBeanMethod() {
147 		initBeanFactory(ConfigWithFinalBean.class);
148 	}
149 
150 	@Test
151 	public void simplestPossibleConfig() {
152 		BeanFactory factory = initBeanFactory(SimplestPossibleConfig.class);
153 		String stringBean = factory.getBean("stringBean", String.class);
154 		assertEquals(stringBean, "foo");
155 	}
156 
157 	@Test
158 	public void configWithObjectReturnType() {
159 		BeanFactory factory = initBeanFactory(ConfigWithNonSpecificReturnTypes.class);
160 		assertEquals(Object.class, factory.getType("stringBean"));
161 		assertFalse(factory.isTypeMatch("stringBean", String.class));
162 		String stringBean = factory.getBean("stringBean", String.class);
163 		assertEquals(stringBean, "foo");
164 	}
165 
166 	@Test
167 	public void configWithFactoryBeanReturnType() {
168 		ListableBeanFactory factory = initBeanFactory(ConfigWithNonSpecificReturnTypes.class);
169 		assertEquals(List.class, factory.getType("factoryBean"));
170 		assertTrue(factory.isTypeMatch("factoryBean", List.class));
171 		assertEquals(FactoryBean.class, factory.getType("&factoryBean"));
172 		assertTrue(factory.isTypeMatch("&factoryBean", FactoryBean.class));
173 		assertFalse(factory.isTypeMatch("&factoryBean", BeanClassLoaderAware.class));
174 		assertFalse(factory.isTypeMatch("&factoryBean", ListFactoryBean.class));
175 		assertTrue(factory.getBean("factoryBean") instanceof List);
176 
177 		String[] beanNames = factory.getBeanNamesForType(FactoryBean.class);
178 		assertEquals(1, beanNames.length);
179 		assertEquals("&factoryBean", beanNames[0]);
180 
181 		beanNames = factory.getBeanNamesForType(BeanClassLoaderAware.class);
182 		assertEquals(1, beanNames.length);
183 		assertEquals("&factoryBean", beanNames[0]);
184 
185 		beanNames = factory.getBeanNamesForType(ListFactoryBean.class);
186 		assertEquals(1, beanNames.length);
187 		assertEquals("&factoryBean", beanNames[0]);
188 
189 		beanNames = factory.getBeanNamesForType(List.class);
190 		assertEquals("factoryBean", beanNames[0]);
191 	}
192 
193 	@Test
194 	public void configurationWithPrototypeScopedBeans() {
195 		BeanFactory factory = initBeanFactory(ConfigWithPrototypeBean.class);
196 
197 		TestBean foo = factory.getBean("foo", TestBean.class);
198 		ITestBean bar = factory.getBean("bar", ITestBean.class);
199 		ITestBean baz = factory.getBean("baz", ITestBean.class);
200 
201 		assertSame(foo.getSpouse(), bar);
202 		assertNotSame(bar.getSpouse(), baz);
203 	}
204 
205 	@Test
206 	public void configurationWithPostProcessor() {
207 		AnnotationConfigApplicationContext factory = new AnnotationConfigApplicationContext();
208 		factory.register(ConfigWithPostProcessor.class);
209 		RootBeanDefinition placeholderConfigurer = new RootBeanDefinition(PropertyPlaceholderConfigurer.class);
210 		placeholderConfigurer.getPropertyValues().add("properties", "myProp=myValue");
211 		factory.registerBeanDefinition("placeholderConfigurer", placeholderConfigurer);
212 		factory.refresh();
213 
214 		TestBean foo = factory.getBean("foo", TestBean.class);
215 		ITestBean bar = factory.getBean("bar", ITestBean.class);
216 		ITestBean baz = factory.getBean("baz", ITestBean.class);
217 
218 		assertEquals("foo-processed-myValue", foo.getName());
219 		assertEquals("bar-processed-myValue", bar.getName());
220 		assertEquals("baz-processed-myValue", baz.getName());
221 
222 		SpousyTestBean listener = factory.getBean("listenerTestBean", SpousyTestBean.class);
223 		assertTrue(listener.refreshed);
224 	}
225 
226 
227 	@Configuration
228 	static class ConfigWithBeanWithCustomName {
229 
230 		static TestBean testBean = new TestBean();
231 
232 		@Bean(name="customName")
233 		public TestBean methodName() {
234 			return testBean;
235 		}
236 	}
237 
238 
239 	@Configuration
240 	static class ConfigWithBeanWithAliases {
241 
242 		static TestBean testBean = new TestBean();
243 
244 		@Bean(name={"name1", "alias1", "alias2", "alias3"})
245 		public TestBean methodName() {
246 			return testBean;
247 		}
248 	}
249 
250 
251 	@Configuration
252 	static class ConfigWithBeanWithProviderImplementation implements Provider<TestBean> {
253 
254 		static TestBean testBean = new TestBean();
255 
256 		@Bean(name="customName")
257 		public TestBean get() {
258 			return testBean;
259 		}
260 	}
261 
262 
263 	@Configuration
264 	static class ConfigWithSetWithProviderImplementation implements Provider<Set<String>> {
265 
266 		static Set<String> set = Collections.singleton("value");
267 
268 		@Bean(name="customName")
269 		public Set<String> get() {
270 			return set;
271 		}
272 	}
273 
274 
275 	@Configuration
276 	static class ConfigWithFinalBean {
277 
278 		public final @Bean TestBean testBean() {
279 			return new TestBean();
280 		}
281 	}
282 
283 
284 	@Configuration
285 	static class SimplestPossibleConfig {
286 
287 		public @Bean String stringBean() {
288 			return "foo";
289 		}
290 	}
291 
292 
293 	@Configuration
294 	static class ConfigWithNonSpecificReturnTypes {
295 
296 		public @Bean Object stringBean() {
297 			return "foo";
298 		}
299 
300 		public @Bean FactoryBean<?> factoryBean() {
301 			ListFactoryBean fb = new ListFactoryBean();
302 			fb.setSourceList(Arrays.asList("element1", "element2"));
303 			return fb;
304 		}
305 	}
306 
307 
308 	@Configuration
309 	static class ConfigWithPrototypeBean {
310 
311 		public @Bean TestBean foo() {
312 			TestBean foo = new SpousyTestBean("foo");
313 			foo.setSpouse(bar());
314 			return foo;
315 		}
316 
317 		public @Bean TestBean bar() {
318 			TestBean bar = new SpousyTestBean("bar");
319 			bar.setSpouse(baz());
320 			return bar;
321 		}
322 
323 		@Bean @Scope("prototype")
324 		public TestBean baz() {
325 			return new TestBean("baz");
326 		}
327 	}
328 
329 
330 	@SuppressWarnings("unused")
331 	static class ConfigWithPostProcessor extends ConfigWithPrototypeBean {
332 
333 		@Value("${myProp}")
334 		private String myProp;
335 
336 		@Bean
337 		public POBPP beanPostProcessor() {
338 			return new POBPP() {
339 
340 				String nameSuffix = "-processed-" + myProp;
341 
342 				public void setNameSuffix(String nameSuffix) {
343 					this.nameSuffix = nameSuffix;
344 				}
345 
346 				@Override
347 				public Object postProcessBeforeInitialization(Object bean, String beanName) {
348 					if (bean instanceof ITestBean) {
349 						((ITestBean) bean).setName(((ITestBean) bean).getName() + nameSuffix);
350 					}
351 					return bean;
352 				}
353 
354 				@Override
355 				public Object postProcessAfterInitialization(Object bean, String beanName) {
356 					return bean;
357 				}
358 
359 				public int getOrder() {
360 					return 0;
361 				}
362 			};
363 		}
364 
365 		//@Bean
366 		public BeanFactoryPostProcessor beanFactoryPostProcessor() {
367 			return new BeanFactoryPostProcessor() {
368 				@Override
369 				public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
370 					BeanDefinition bd = beanFactory.getBeanDefinition("beanPostProcessor");
371 					bd.getPropertyValues().addPropertyValue("nameSuffix", "-processed-" + myProp);
372 				}
373 			};
374 		}
375 
376 		@Bean
377 		public ITestBean listenerTestBean() {
378 			return new SpousyTestBean("listener");
379 		}
380 	}
381 
382 
383 	public interface POBPP extends BeanPostProcessor {
384 	}
385 
386 
387 	private static class SpousyTestBean extends TestBean implements ApplicationListener<ContextRefreshedEvent> {
388 
389 		public boolean refreshed = false;
390 
391 		public SpousyTestBean(String name) {
392 			super(name);
393 		}
394 
395 		@Override
396 		@Required
397 		public void setSpouse(ITestBean spouse) {
398 			super.setSpouse(spouse);
399 		}
400 
401 		@Override
402 		public void onApplicationEvent(ContextRefreshedEvent event) {
403 			this.refreshed = true;
404 		}
405 	}
406 
407 }