/*
* Copyright 2002-2017 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.Map;
import java.util.regex.Pattern;
import org.junit.Test;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation6.ComponentForScanning;
import org.springframework.context.annotation6.ConfigForScanning;
import org.springframework.context.annotation6.Jsr330NamedForScanning;
import static java.lang.String.format;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.springframework.util.StringUtils.*;
/**
* @author Chris Beams
* @author Juergen Hoeller
*/
public class AnnotationConfigApplicationContextTests {
@Test(expected = IllegalArgumentException.class)
public void nullGetBeanParameterIsDisallowed() {
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
context.getBean((Class<?>) null);
}
@Test
public void scanAndRefresh() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.scan("org.springframework.context.annotation6");
context.refresh();
context.getBean(uncapitalize(ConfigForScanning.class.getSimpleName()));
context.getBean("testBean"); // contributed by ConfigForScanning
context.getBean(uncapitalize(ComponentForScanning.class.getSimpleName()));
context.getBean(uncapitalize(Jsr330NamedForScanning.class.getSimpleName()));
Map<String, Object> beans = context.getBeansWithAnnotation(Configuration.class);
assertEquals(1, beans.size());
}
@Test
public void registerAndRefresh() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(Config.class, NameConfig.class);
context.refresh();
context.getBean("testBean");
context.getBean("name");
Map<String, Object> beans = context.getBeansWithAnnotation(Configuration.class);
assertEquals(2, beans.size());
}
@Test
public void getBeansWithAnnotation() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(Config.class, NameConfig.class, UntypedFactoryBean.class);
context.refresh();
context.getBean("testBean");
context.getBean("name");
Map<String, Object> beans = context.getBeansWithAnnotation(Configuration.class);
assertEquals(2, beans.size());
}
@Test
public void getBeanByType() {
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
TestBean testBean = context.getBean(TestBean.class);
assertNotNull(testBean);
assertThat(testBean.name, equalTo("foo"));
}
/**
* Tests that Configuration classes are registered according to convention
* @see org.springframework.beans.factory.support.DefaultBeanNameGenerator#generateBeanName
*/
@Test
public void defaultConfigClassBeanNameIsGeneratedProperly() {
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
// attempt to retrieve the instance by its generated bean name
Config configObject = (Config) context.getBean("annotationConfigApplicationContextTests.Config");
assertNotNull(configObject);
}
/**
* Tests that specifying @Configuration(value="foo") results in registering
* the configuration class with bean name 'foo'.
*/
@Test
public void explicitConfigClassBeanNameIsRespected() {
ApplicationContext context = new AnnotationConfigApplicationContext(ConfigWithCustomName.class);
// attempt to retrieve the instance by its specified name
ConfigWithCustomName configObject = (ConfigWithCustomName) context.getBean("customConfigBeanName");
assertNotNull(configObject);
}
@Test
public void individualBeans() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(BeanA.class, BeanB.class, BeanC.class);
context.refresh();
assertSame(context.getBean(BeanB.class), context.getBean(BeanA.class).b);
assertSame(context.getBean(BeanC.class), context.getBean(BeanA.class).c);
assertSame(context, context.getBean(BeanB.class).applicationContext);
}
@Test
public void individualNamedBeans() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.registerBean("a", BeanA.class);
context.registerBean("b", BeanB.class);
context.registerBean("c", BeanC.class);
context.refresh();
assertSame(context.getBean("b"), context.getBean("a", BeanA.class).b);
assertSame(context.getBean("c"), context.getBean("a", BeanA.class).c);
assertSame(context, context.getBean("b", BeanB.class).applicationContext);
}
@Test
public void individualBeanWithSupplier() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.registerBean(BeanA.class,
() -> new BeanA(context.getBean(BeanB.class), context.getBean(BeanC.class)));
context.registerBean(BeanB.class, BeanB::new);
context.registerBean(BeanC.class, BeanC::new);
context.refresh();
assertTrue(context.getBeanFactory().containsSingleton("annotationConfigApplicationContextTests.BeanA"));
assertSame(context.getBean(BeanB.class), context.getBean(BeanA.class).b);
assertSame(context.getBean(BeanC.class), context.getBean(BeanA.class).c);
assertSame(context, context.getBean(BeanB.class).applicationContext);
assertArrayEquals(new String[] {"annotationConfigApplicationContextTests.BeanA"},
context.getDefaultListableBeanFactory().getDependentBeans("annotationConfigApplicationContextTests.BeanB"));
assertArrayEquals(new String[] {"annotationConfigApplicationContextTests.BeanA"},
context.getDefaultListableBeanFactory().getDependentBeans("annotationConfigApplicationContextTests.BeanC"));
}
@Test
public void individualBeanWithSupplierAndCustomizer() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.registerBean(BeanA.class,
() -> new BeanA(context.getBean(BeanB.class), context.getBean(BeanC.class)),
bd -> bd.setLazyInit(true));
context.registerBean(BeanB.class, BeanB::new);
context.registerBean(BeanC.class, BeanC::new);
context.refresh();
assertFalse(context.getBeanFactory().containsSingleton("annotationConfigApplicationContextTests.BeanA"));
assertSame(context.getBean(BeanB.class), context.getBean(BeanA.class).b);
assertSame(context.getBean(BeanC.class), context.getBean(BeanA.class).c);
assertSame(context, context.getBean(BeanB.class).applicationContext);
}
@Test
public void individualNamedBeanWithSupplier() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.registerBean("a", BeanA.class,
() -> new BeanA(context.getBean(BeanB.class), context.getBean(BeanC.class)));
context.registerBean("b", BeanB.class, BeanB::new);
context.registerBean("c", BeanC.class, BeanC::new);
context.refresh();
assertTrue(context.getBeanFactory().containsSingleton("a"));
assertSame(context.getBean("b", BeanB.class), context.getBean(BeanA.class).b);
assertSame(context.getBean("c"), context.getBean("a", BeanA.class).c);
assertSame(context, context.getBean("b", BeanB.class).applicationContext);
}
@Test
public void individualNamedBeanWithSupplierAndCustomizer() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.registerBean("a", BeanA.class,
() -> new BeanA(context.getBean(BeanB.class), context.getBean(BeanC.class)),
bd -> bd.setLazyInit(true));
context.registerBean("b", BeanB.class, BeanB::new);
context.registerBean("c", BeanC.class, BeanC::new);
context.refresh();
assertFalse(context.getBeanFactory().containsSingleton("a"));
assertSame(context.getBean("b", BeanB.class), context.getBean(BeanA.class).b);
assertSame(context.getBean("c"), context.getBean("a", BeanA.class).c);
assertSame(context, context.getBean("b", BeanB.class).applicationContext);
}
@Test
public void individualBeanWithSpecifiedConstructorArguments() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
BeanB b = new BeanB();
BeanC c = new BeanC();
context.registerBean(BeanA.class, b, c);
context.refresh();
assertSame(b, context.getBean(BeanA.class).b);
assertSame(c, context.getBean(BeanA.class).c);
assertNull(b.applicationContext);
}
@Test
public void individualNamedBeanWithSpecifiedConstructorArguments() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
BeanB b = new BeanB();
BeanC c = new BeanC();
context.registerBean("a", BeanA.class, b, c);
context.refresh();
assertSame(b, context.getBean("a", BeanA.class).b);
assertSame(c, context.getBean("a", BeanA.class).c);
assertNull(b.applicationContext);
}
@Test
public void individualBeanWithMixedConstructorArguments() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
BeanC c = new BeanC();
context.registerBean(BeanA.class, c);
context.registerBean(BeanB.class);
context.refresh();
assertSame(context.getBean(BeanB.class), context.getBean(BeanA.class).b);
assertSame(c, context.getBean(BeanA.class).c);
assertSame(context, context.getBean(BeanB.class).applicationContext);
}
@Test
public void individualNamedBeanWithMixedConstructorArguments() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
BeanC c = new BeanC();
context.registerBean("a", BeanA.class, c);
context.registerBean("b", BeanB.class);
context.refresh();
assertSame(context.getBean("b", BeanB.class), context.getBean("a", BeanA.class).b);
assertSame(c, context.getBean("a", BeanA.class).c);
assertSame(context, context.getBean("b", BeanB.class).applicationContext);
}
@Test
public void getBeanByTypeRaisesNoSuchBeanDefinitionException() {
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
// attempt to retrieve a bean that does not exist
Class<?> targetType = Pattern.class;
try {
context.getBean(targetType);
fail("Should have thrown NoSuchBeanDefinitionException");
}
catch (NoSuchBeanDefinitionException ex) {
assertThat(ex.getMessage(), containsString(format("No qualifying bean of type '%s'", targetType.getName())));
}
}
@Test
public void getBeanByTypeAmbiguityRaisesException() {
ApplicationContext context = new AnnotationConfigApplicationContext(TwoTestBeanConfig.class);
try {
context.getBean(TestBean.class);
}
catch (NoSuchBeanDefinitionException ex) {
assertThat(ex.getMessage(),
allOf(
containsString("No qualifying bean of type '" + TestBean.class.getName() + "'"),
containsString("tb1"),
containsString("tb2")
)
);
}
}
@Test
public void autowiringIsEnabledByDefault() {
ApplicationContext context = new AnnotationConfigApplicationContext(AutowiredConfig.class);
assertThat(context.getBean(TestBean.class).name, equalTo("foo"));
}
@Test
public void nullReturningBeanPostProcessor() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(AutowiredConfig.class);
context.getBeanFactory().addBeanPostProcessor(new BeanPostProcessor() {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return (bean instanceof TestBean ? null : bean);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
});
context.getBeanFactory().addBeanPostProcessor(new BeanPostProcessor() {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
bean.getClass().getName();
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
bean.getClass().getName();
return bean;
}
});
context.refresh();
}
@Configuration
static class Config {
@Bean
public TestBean testBean() {
TestBean testBean = new TestBean();
testBean.name = "foo";
return testBean;
}
}
@Configuration("customConfigBeanName")
static class ConfigWithCustomName {
@Bean
public TestBean testBean() {
return new TestBean();
}
}
static class ConfigMissingAnnotation {
@Bean
public TestBean testBean() {
return new TestBean();
}
}
@Configuration
static class TwoTestBeanConfig {
@Bean TestBean tb1() {
return new TestBean();
}
@Bean TestBean tb2() {
return new TestBean();
}
}
@Configuration
static class NameConfig {
@Bean String name() { return "foo"; }
}
@Configuration
@Import(NameConfig.class)
static class AutowiredConfig {
@Autowired String autowiredName;
@Bean TestBean testBean() {
TestBean testBean = new TestBean();
testBean.name = autowiredName;
return testBean;
}
}
static class BeanA {
BeanB b;
BeanC c;
@Autowired public BeanA(BeanB b, BeanC c) {
this.b = b;
this.c = c;
}
}
static class BeanB {
@Autowired ApplicationContext applicationContext;
public BeanB() {
}
}
static class BeanC {}
static class UntypedFactoryBean implements FactoryBean<Object> {
@Override
public Object getObject() {
return null;
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public boolean isSingleton() {
return false;
}
}
}
class TestBean {
String name;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (name == null ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
TestBean other = (TestBean) obj;
if (name == null) {
if (other.name != null)
return false;
}
else if (!name.equals(other.name))
return false;
return true;
}
}