/* * Copyright 2002-2010 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.configuration; import static org.junit.Assert.*; import org.junit.Test; import test.beans.ITestBean; import test.beans.TestBean; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Required; import org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor; import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; import org.springframework.beans.factory.parsing.BeanDefinitionParsingException; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.ApplicationListener; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.AnnotationConfigUtils; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ConfigurationClassPostProcessor; import org.springframework.context.annotation.Scope; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.support.GenericApplicationContext; import org.springframework.core.PriorityOrdered; /** * Miscellaneous system tests covering {@link Bean} naming, aliases, scoping and error * handling within {@link Configuration} class definitions. * * @author Chris Beams * @author Juergen Hoeller */ public class ConfigurationClassProcessingTests { /** * Creates a new {@link BeanFactory}, populates it with a {@link BeanDefinition} for * each of the given {@link Configuration} <var>configClasses</var>, and then * post-processes the factory using JavaConfig's {@link ConfigurationClassPostProcessor}. * When complete, the factory is ready to service requests for any {@link Bean} methods * declared by <var>configClasses</var>. */ private BeanFactory initBeanFactory(Class<?>... configClasses) { DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); for (Class<?> configClass : configClasses) { String configBeanName = configClass.getName(); factory.registerBeanDefinition(configBeanName, new RootBeanDefinition(configClass)); } ConfigurationClassPostProcessor ccpp = new ConfigurationClassPostProcessor(); ccpp.postProcessBeanDefinitionRegistry(factory); ccpp.postProcessBeanFactory(factory); RequiredAnnotationBeanPostProcessor rapp = new RequiredAnnotationBeanPostProcessor(); rapp.setBeanFactory(factory); factory.addBeanPostProcessor(rapp); return factory; } @Test public void customBeanNameIsRespected() { GenericApplicationContext ac = new GenericApplicationContext(); AnnotationConfigUtils.registerAnnotationConfigProcessors(ac); ac.registerBeanDefinition("config", new RootBeanDefinition(ConfigWithBeanWithCustomName.class)); ac.refresh(); assertSame(ac.getBean("customName"), ConfigWithBeanWithCustomName.testBean); // method name should not be registered try { ac.getBean("methodName"); fail("bean should not have been registered with 'methodName'"); } catch (NoSuchBeanDefinitionException ex) { // expected } } @Test public void aliasesAreRespected() { BeanFactory factory = initBeanFactory(ConfigWithBeanWithAliases.class); assertSame(factory.getBean("name1"), ConfigWithBeanWithAliases.testBean); String[] aliases = factory.getAliases("name1"); for(String alias : aliases) assertSame(factory.getBean(alias), ConfigWithBeanWithAliases.testBean); // method name should not be registered try { factory.getBean("methodName"); fail("bean should not have been registered with 'methodName'"); } catch (NoSuchBeanDefinitionException ex) { /* expected */ } } @Test(expected=BeanDefinitionParsingException.class) public void testFinalBeanMethod() { initBeanFactory(ConfigWithFinalBean.class); } @Test public void simplestPossibleConfiguration() { BeanFactory factory = initBeanFactory(SimplestPossibleConfig.class); String stringBean = factory.getBean("stringBean", String.class); assertEquals(stringBean, "foo"); } @Test public void configurationWithPrototypeScopedBeans() { BeanFactory factory = initBeanFactory(ConfigWithPrototypeBean.class); TestBean foo = factory.getBean("foo", TestBean.class); ITestBean bar = factory.getBean("bar", ITestBean.class); ITestBean baz = factory.getBean("baz", ITestBean.class); assertSame(foo.getSpouse(), bar); assertNotSame(bar.getSpouse(), baz); } @Test public void configurationWithPostProcessor() { AnnotationConfigApplicationContext factory = new AnnotationConfigApplicationContext(); factory.register(ConfigWithPostProcessor.class); RootBeanDefinition placeholderConfigurer = new RootBeanDefinition(PropertyPlaceholderConfigurer.class); placeholderConfigurer.getPropertyValues().add("properties", "myProp=myValue"); factory.registerBeanDefinition("placeholderConfigurer", placeholderConfigurer); factory.refresh(); TestBean foo = factory.getBean("foo", TestBean.class); ITestBean bar = factory.getBean("bar", ITestBean.class); ITestBean baz = factory.getBean("baz", ITestBean.class); assertEquals("foo-processed-myValue", foo.getName()); assertEquals("bar-processed-myValue", bar.getName()); assertEquals("baz-processed-myValue", baz.getName()); SpousyTestBean listener = factory.getBean("listenerTestBean", SpousyTestBean.class); assertTrue(listener.refreshed); } @Configuration static class ConfigWithBeanWithCustomName { static TestBean testBean = new TestBean(); @Bean(name="customName") public TestBean methodName() { return testBean; } } @Configuration static class ConfigWithFinalBean { public final @Bean TestBean testBean() { return new TestBean(); } } @Configuration static class SimplestPossibleConfig { public @Bean String stringBean() { return "foo"; } } @Configuration static class ConfigWithBeanWithAliases { static TestBean testBean = new TestBean(); @Bean(name={"name1", "alias1", "alias2", "alias3"}) public TestBean methodName() { return testBean; } } @Configuration static class ConfigWithPrototypeBean { public @Bean TestBean foo() { TestBean foo = new SpousyTestBean("foo"); foo.setSpouse(bar()); return foo; } public @Bean TestBean bar() { TestBean bar = new SpousyTestBean("bar"); bar.setSpouse(baz()); return bar; } @Bean @Scope("prototype") public TestBean baz() { return new TestBean("baz"); } } static class ConfigWithPostProcessor extends ConfigWithPrototypeBean { @Value("${myProp}") private String myProp; @Bean public POBPP beanPostProcessor() { return new POBPP() { String nameSuffix = "-processed-" + myProp; public void setNameSuffix(String nameSuffix) { this.nameSuffix = nameSuffix; } public Object postProcessBeforeInitialization(Object bean, String beanName) { if (bean instanceof ITestBean) { ((ITestBean) bean).setName(((ITestBean) bean).getName() + nameSuffix); } return bean; } public Object postProcessAfterInitialization(Object bean, String beanName) { return bean; } public int getOrder() { return 0; } }; } //@Bean public BeanFactoryPostProcessor beanFactoryPostProcessor() { return new BeanFactoryPostProcessor() { public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { BeanDefinition bd = beanFactory.getBeanDefinition("beanPostProcessor"); bd.getPropertyValues().addPropertyValue("nameSuffix", "-processed-" + myProp); } }; } @Bean public ITestBean listenerTestBean() { return new SpousyTestBean("listener"); } } public interface POBPP extends BeanPostProcessor { } private static class SpousyTestBean extends TestBean implements ApplicationListener<ContextRefreshedEvent> { public boolean refreshed = false; public SpousyTestBean(String name) { super(name); } @Override @Required public void setSpouse(ITestBean spouse) { super.setSpouse(spouse); } public void onApplicationEvent(ContextRefreshedEvent event) { this.refreshed = true; } } }