/*
* Copyright 2002-2009 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 example.scannable.CustomComponent;
import example.scannable.FooService;
import example.scannable.FooServiceImpl;
import example.scannable.NamedStubDao;
import example.scannable.StubFooDao;
import org.aspectj.lang.annotation.Aspect;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.beans.TestBean;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.support.StaticListableBeanFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation2.NamedStubDao2;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.stereotype.Component;
/**
* @author Mark Fisher
* @author Juergen Hoeller
* @author Chris Beams
*/
public class ClassPathBeanDefinitionScannerTests {
private static final String BASE_PACKAGE = "example.scannable";
@Test
public void testSimpleScanWithDefaultFiltersAndPostProcessors() {
GenericApplicationContext context = new GenericApplicationContext();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
int beanCount = scanner.scan(BASE_PACKAGE);
assertEquals(10, beanCount);
assertTrue(context.containsBean("serviceInvocationCounter"));
assertTrue(context.containsBean("fooServiceImpl"));
assertTrue(context.containsBean("stubFooDao"));
assertTrue(context.containsBean("myNamedComponent"));
assertTrue(context.containsBean("myNamedDao"));
assertTrue(context.containsBean("thoreau"));
assertTrue(context.containsBean(AnnotationConfigUtils.CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
assertTrue(context.containsBean(AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
assertTrue(context.containsBean(AnnotationConfigUtils.REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
assertTrue(context.containsBean(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
context.refresh();
FooServiceImpl service = context.getBean("fooServiceImpl", FooServiceImpl.class);
assertTrue(context.getDefaultListableBeanFactory().containsSingleton("myNamedComponent"));
assertEquals("bar", service.foo(1));
assertTrue(context.isPrototype("thoreau"));
}
@Test
public void testSimpleScanWithDefaultFiltersAndPrimaryLazyBean() {
GenericApplicationContext context = new GenericApplicationContext();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.scan(BASE_PACKAGE);
scanner.scan("org.springframework.context.annotation5");
assertTrue(context.containsBean("serviceInvocationCounter"));
assertTrue(context.containsBean("fooServiceImpl"));
assertTrue(context.containsBean("stubFooDao"));
assertTrue(context.containsBean("myNamedComponent"));
assertTrue(context.containsBean("myNamedDao"));
assertTrue(context.containsBean("otherFooDao"));
context.refresh();
assertFalse(context.getBeanFactory().containsSingleton("otherFooDao"));
assertFalse(context.getBeanFactory().containsSingleton("fooServiceImpl"));
FooServiceImpl service = context.getBean("fooServiceImpl", FooServiceImpl.class);
assertTrue(context.getBeanFactory().containsSingleton("otherFooDao"));
assertEquals("other", service.foo(1));
}
@Test
public void testDoubleScan() {
GenericApplicationContext context = new GenericApplicationContext();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
int beanCount = scanner.scan(BASE_PACKAGE);
assertEquals(10, beanCount);
scanner.scan(BASE_PACKAGE);
assertTrue(context.containsBean("serviceInvocationCounter"));
assertTrue(context.containsBean("fooServiceImpl"));
assertTrue(context.containsBean("stubFooDao"));
assertTrue(context.containsBean("myNamedComponent"));
assertTrue(context.containsBean("myNamedDao"));
assertTrue(context.containsBean("thoreau"));
}
@Test
public void testSimpleScanWithDefaultFiltersAndNoPostProcessors() {
GenericApplicationContext context = new GenericApplicationContext();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.setIncludeAnnotationConfig(false);
int beanCount = scanner.scan(BASE_PACKAGE);
assertEquals(6, beanCount);
assertTrue(context.containsBean("serviceInvocationCounter"));
assertTrue(context.containsBean("fooServiceImpl"));
assertTrue(context.containsBean("stubFooDao"));
assertTrue(context.containsBean("myNamedComponent"));
assertTrue(context.containsBean("myNamedDao"));
}
@Test
public void testSimpleScanWithDefaultFiltersAndOverridingBean() {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBeanDefinition("stubFooDao", new RootBeanDefinition(TestBean.class));
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.setIncludeAnnotationConfig(false);
// should not fail!
scanner.scan(BASE_PACKAGE);
}
@Test
public void testSimpleScanWithDefaultFiltersAndDefaultBeanNameClash() {
GenericApplicationContext context = new GenericApplicationContext();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.setIncludeAnnotationConfig(false);
try {
scanner.scan("org.springframework.context.annotation3");
scanner.scan(BASE_PACKAGE);
fail("Should have thrown IllegalStateException");
}
catch (IllegalStateException ex) {
// expected
assertTrue(ex.getMessage().contains("stubFooDao"));
assertTrue(ex.getMessage().contains(StubFooDao.class.getName()));
}
}
@Test
public void testSimpleScanWithDefaultFiltersAndOverriddenEqualNamedBean() {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBeanDefinition("myNamedDao", new RootBeanDefinition(NamedStubDao.class));
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.setIncludeAnnotationConfig(false);
int beanCount = scanner.scan(BASE_PACKAGE);
assertEquals(5, beanCount);
assertEquals(6, context.getBeanDefinitionCount());
assertTrue(context.containsBean("serviceInvocationCounter"));
assertTrue(context.containsBean("fooServiceImpl"));
assertTrue(context.containsBean("stubFooDao"));
assertTrue(context.containsBean("myNamedComponent"));
assertTrue(context.containsBean("myNamedDao"));
}
@Test
public void testSimpleScanWithDefaultFiltersAndOverriddenCompatibleNamedBean() {
GenericApplicationContext context = new GenericApplicationContext();
RootBeanDefinition bd = new RootBeanDefinition(NamedStubDao.class);
bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
context.registerBeanDefinition("myNamedDao", bd);
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.setIncludeAnnotationConfig(false);
int beanCount = scanner.scan(BASE_PACKAGE);
assertEquals(5, beanCount);
assertEquals(6, context.getBeanDefinitionCount());
assertTrue(context.containsBean("serviceInvocationCounter"));
assertTrue(context.containsBean("fooServiceImpl"));
assertTrue(context.containsBean("stubFooDao"));
assertTrue(context.containsBean("myNamedComponent"));
assertTrue(context.containsBean("myNamedDao"));
}
@Test
public void testSimpleScanWithDefaultFiltersAndSameBeanTwice() {
GenericApplicationContext context = new GenericApplicationContext();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.setIncludeAnnotationConfig(false);
// should not fail!
scanner.scan(BASE_PACKAGE);
scanner.scan(BASE_PACKAGE);
}
@Test
public void testSimpleScanWithDefaultFiltersAndSpecifiedBeanNameClash() {
GenericApplicationContext context = new GenericApplicationContext();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.setIncludeAnnotationConfig(false);
try {
scanner.scan("org.springframework.context.annotation2");
scanner.scan(BASE_PACKAGE);
fail("Must have thrown IllegalStateException");
}
catch (IllegalStateException expected) {
assertTrue(expected.getMessage().contains("myNamedDao"));
assertTrue(expected.getMessage().contains(NamedStubDao.class.getName()));
assertTrue(expected.getMessage().contains(NamedStubDao2.class.getName()));
}
}
@Test
public void testCustomIncludeFilterWithoutDefaultsButIncludingPostProcessors() {
GenericApplicationContext context = new GenericApplicationContext();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context, false);
scanner.addIncludeFilter(new AnnotationTypeFilter(CustomComponent.class));
int beanCount = scanner.scan(BASE_PACKAGE);
assertEquals(5, beanCount);
assertTrue(context.containsBean("messageBean"));
assertTrue(context.containsBean(AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
assertTrue(context.containsBean(AnnotationConfigUtils.REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
assertTrue(context.containsBean(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
}
@Test
public void testCustomIncludeFilterWithoutDefaultsAndNoPostProcessors() {
GenericApplicationContext context = new GenericApplicationContext();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context, false);
scanner.addIncludeFilter(new AnnotationTypeFilter(CustomComponent.class));
int beanCount = scanner.scan(BASE_PACKAGE);
assertEquals(5, beanCount);
assertTrue(context.containsBean("messageBean"));
assertFalse(context.containsBean("serviceInvocationCounter"));
assertFalse(context.containsBean("fooServiceImpl"));
assertFalse(context.containsBean("stubFooDao"));
assertFalse(context.containsBean("myNamedComponent"));
assertFalse(context.containsBean("myNamedDao"));
assertTrue(context.containsBean(AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
assertTrue(context.containsBean(AnnotationConfigUtils.REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
assertTrue(context.containsBean(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
}
@Test
public void testCustomIncludeFilterAndDefaults() {
GenericApplicationContext context = new GenericApplicationContext();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context, true);
scanner.addIncludeFilter(new AnnotationTypeFilter(CustomComponent.class));
int beanCount = scanner.scan(BASE_PACKAGE);
assertEquals(11, beanCount);
assertTrue(context.containsBean("messageBean"));
assertTrue(context.containsBean("serviceInvocationCounter"));
assertTrue(context.containsBean("fooServiceImpl"));
assertTrue(context.containsBean("stubFooDao"));
assertTrue(context.containsBean("myNamedComponent"));
assertTrue(context.containsBean("myNamedDao"));
assertTrue(context.containsBean(AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
assertTrue(context.containsBean(AnnotationConfigUtils.REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
assertTrue(context.containsBean(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
}
@Test
public void testCustomAnnotationExcludeFilterAndDefaults() {
GenericApplicationContext context = new GenericApplicationContext();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context, true);
scanner.addExcludeFilter(new AnnotationTypeFilter(Aspect.class));
int beanCount = scanner.scan(BASE_PACKAGE);
assertEquals(9, beanCount);
assertFalse(context.containsBean("serviceInvocationCounter"));
assertTrue(context.containsBean("fooServiceImpl"));
assertTrue(context.containsBean("stubFooDao"));
assertTrue(context.containsBean("myNamedComponent"));
assertTrue(context.containsBean("myNamedDao"));
assertTrue(context.containsBean(AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
assertTrue(context.containsBean(AnnotationConfigUtils.REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
assertTrue(context.containsBean(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
}
@Test
public void testCustomAssignableTypeExcludeFilterAndDefaults() {
GenericApplicationContext context = new GenericApplicationContext();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context, true);
scanner.addExcludeFilter(new AssignableTypeFilter(FooService.class));
int beanCount = scanner.scan(BASE_PACKAGE);
assertEquals(9, beanCount);
assertFalse(context.containsBean("fooServiceImpl"));
assertTrue(context.containsBean("serviceInvocationCounter"));
assertTrue(context.containsBean("stubFooDao"));
assertTrue(context.containsBean("myNamedComponent"));
assertTrue(context.containsBean("myNamedDao"));
assertTrue(context.containsBean(AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
assertTrue(context.containsBean(AnnotationConfigUtils.REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
assertTrue(context.containsBean(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
}
@Test
public void testCustomAssignableTypeExcludeFilterAndDefaultsWithoutPostProcessors() {
GenericApplicationContext context = new GenericApplicationContext();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context, true);
scanner.setIncludeAnnotationConfig(false);
scanner.addExcludeFilter(new AssignableTypeFilter(FooService.class));
int beanCount = scanner.scan(BASE_PACKAGE);
assertEquals(5, beanCount);
assertFalse(context.containsBean("fooServiceImpl"));
assertTrue(context.containsBean("serviceInvocationCounter"));
assertTrue(context.containsBean("stubFooDao"));
assertTrue(context.containsBean("myNamedComponent"));
assertTrue(context.containsBean("myNamedDao"));
assertFalse(context.containsBean(AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
assertFalse(context.containsBean(AnnotationConfigUtils.REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
assertFalse(context.containsBean(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
}
@Test
public void testMultipleCustomExcludeFiltersAndDefaults() {
GenericApplicationContext context = new GenericApplicationContext();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context, true);
scanner.addExcludeFilter(new AssignableTypeFilter(FooService.class));
scanner.addExcludeFilter(new AnnotationTypeFilter(Aspect.class));
int beanCount = scanner.scan(BASE_PACKAGE);
assertEquals(8, beanCount);
assertFalse(context.containsBean("fooServiceImpl"));
assertFalse(context.containsBean("serviceInvocationCounter"));
assertTrue(context.containsBean("stubFooDao"));
assertTrue(context.containsBean("myNamedComponent"));
assertTrue(context.containsBean("myNamedDao"));
assertTrue(context.containsBean(AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
assertTrue(context.containsBean(AnnotationConfigUtils.REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
assertTrue(context.containsBean(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
}
@Test
public void testCustomBeanNameGenerator() {
GenericApplicationContext context = new GenericApplicationContext();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.setBeanNameGenerator(new TestBeanNameGenerator());
int beanCount = scanner.scan(BASE_PACKAGE);
assertEquals(10, beanCount);
assertFalse(context.containsBean("fooServiceImpl"));
assertTrue(context.containsBean("fooService"));
assertTrue(context.containsBean("serviceInvocationCounter"));
assertTrue(context.containsBean("stubFooDao"));
assertTrue(context.containsBean("myNamedComponent"));
assertTrue(context.containsBean("myNamedDao"));
assertTrue(context.containsBean(AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
assertTrue(context.containsBean(AnnotationConfigUtils.REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
assertTrue(context.containsBean(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
}
@Test
public void testMultipleBasePackagesWithDefaultsOnly() {
GenericApplicationContext singlePackageContext = new GenericApplicationContext();
ClassPathBeanDefinitionScanner singlePackageScanner = new ClassPathBeanDefinitionScanner(singlePackageContext);
GenericApplicationContext multiPackageContext = new GenericApplicationContext();
ClassPathBeanDefinitionScanner multiPackageScanner = new ClassPathBeanDefinitionScanner(multiPackageContext);
int singlePackageBeanCount = singlePackageScanner.scan(BASE_PACKAGE);
assertEquals(10, singlePackageBeanCount);
multiPackageScanner.scan(BASE_PACKAGE, "org.springframework.dao.annotation");
// assertTrue(multiPackageBeanCount > singlePackageBeanCount);
}
@Test
public void testMultipleScanCalls() {
GenericApplicationContext context = new GenericApplicationContext();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
int beanCount = scanner.scan(BASE_PACKAGE);
assertEquals(10, beanCount);
assertEquals(beanCount, context.getBeanDefinitionCount());
int addedBeanCount = scanner.scan("org.springframework.aop.aspectj.annotation");
assertEquals(beanCount + addedBeanCount, context.getBeanDefinitionCount());
}
@Test
public void testBeanAutowiredWithAnnotationConfigEnabled() {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBeanDefinition("myBf", new RootBeanDefinition(StaticListableBeanFactory.class));
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.setBeanNameGenerator(new TestBeanNameGenerator());
int beanCount = scanner.scan(BASE_PACKAGE);
assertEquals(10, beanCount);
context.refresh();
FooServiceImpl fooService = context.getBean("fooService", FooServiceImpl.class);
StaticListableBeanFactory myBf = (StaticListableBeanFactory) context.getBean("myBf");
MessageSource ms = (MessageSource) context.getBean("messageSource");
assertTrue(fooService.isInitCalled());
assertEquals("bar", fooService.foo(123));
assertSame(context.getDefaultListableBeanFactory(), fooService.beanFactory);
assertEquals(2, fooService.listableBeanFactory.size());
assertSame(context.getDefaultListableBeanFactory(), fooService.listableBeanFactory.get(0));
assertSame(myBf, fooService.listableBeanFactory.get(1));
assertSame(context, fooService.resourceLoader);
assertSame(context, fooService.resourcePatternResolver);
assertSame(context, fooService.eventPublisher);
assertSame(ms, fooService.messageSource);
assertSame(context, fooService.context);
assertEquals(1, fooService.configurableContext.length);
assertSame(context, fooService.configurableContext[0]);
assertSame(context, fooService.genericContext);
}
@Test
public void testBeanNotAutowiredWithAnnotationConfigDisabled() {
GenericApplicationContext context = new GenericApplicationContext();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.setIncludeAnnotationConfig(false);
scanner.setBeanNameGenerator(new TestBeanNameGenerator());
int beanCount = scanner.scan(BASE_PACKAGE);
assertEquals(6, beanCount);
context.refresh();
FooService fooService = (FooService) context.getBean("fooService");
assertFalse(fooService.isInitCalled());
try {
fooService.foo(123);
fail("NullPointerException expected; fooDao must not have been set");
}
catch (NullPointerException expected) {
}
}
@Test
public void testAutowireCandidatePatternMatches() {
GenericApplicationContext context = new GenericApplicationContext();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.setIncludeAnnotationConfig(true);
scanner.setBeanNameGenerator(new TestBeanNameGenerator());
scanner.setAutowireCandidatePatterns(new String[] { "*FooDao" });
scanner.scan(BASE_PACKAGE);
context.refresh();
FooService fooService = (FooService) context.getBean("fooService");
assertEquals("bar", fooService.foo(123));
}
@Test
public void testAutowireCandidatePatternDoesNotMatch() {
GenericApplicationContext context = new GenericApplicationContext();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.setIncludeAnnotationConfig(true);
scanner.setBeanNameGenerator(new TestBeanNameGenerator());
scanner.setAutowireCandidatePatterns(new String[] { "*NoSuchDao" });
scanner.scan(BASE_PACKAGE);
try {
context.refresh();
context.getBean("fooService");
fail("BeanCreationException expected; fooDao should not have been an autowire-candidate");
}
catch (BeanCreationException expected) {
assertTrue(expected.getMostSpecificCause() instanceof NoSuchBeanDefinitionException);
}
}
private static class TestBeanNameGenerator extends AnnotationBeanNameGenerator {
@Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
String beanName = super.generateBeanName(definition, registry);
return beanName.replace("Impl", "");
}
}
@Component("toBeIgnored")
public class NonStaticInnerClass {
}
}