diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java new file mode 100644 index 00000000000..3d73bf2d5e9 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java @@ -0,0 +1,132 @@ +/* + * Copyright 2002-2011 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.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.BeanNameGenerator; +import org.springframework.context.annotation.ComponentScan.Filter; +import org.springframework.core.env.Environment; +import org.springframework.core.io.ResourceLoader; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.core.type.filter.AnnotationTypeFilter; +import org.springframework.core.type.filter.AssignableTypeFilter; +import org.springframework.core.type.filter.TypeFilter; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * Parser for the @{@link ComponentScan} annotation. + * + * @author Chris Beams + * @since 3.1 + * @see ClassPathBeanDefinitionScanner#scan(String...) + * @see ComponentScanBeanDefinitionParser + */ +class ComponentScanAnnotationParser { + + private final ResourceLoader resourceLoader; + private final Environment environment; + private final BeanDefinitionRegistry registry; + + public ComponentScanAnnotationParser(ResourceLoader resourceLoader, Environment environment, BeanDefinitionRegistry registry) { + this.resourceLoader = resourceLoader; + this.environment = environment; + this.registry = registry; + } + + public void parse(AnnotationMetadata annotationMetadata) { + Map attribs = annotationMetadata.getAnnotationAttributes(ComponentScan.class.getName()); + if (attribs == null) { + // @ComponentScan annotation is not present -> do nothing + return; + } + + ClassPathBeanDefinitionScanner scanner = + new ClassPathBeanDefinitionScanner(registry, (Boolean)attribs.get("useDefaultFilters")); + + Assert.notNull(this.environment, "Environment must not be null"); + scanner.setEnvironment(this.environment); + + Assert.notNull(this.resourceLoader, "ResourceLoader must not be null"); + scanner.setResourceLoader(this.resourceLoader); + + scanner.setBeanNameGenerator(BeanUtils.instantiateClass( + (Class)attribs.get("nameGenerator"), BeanNameGenerator.class)); + + ScopedProxyMode scopedProxyMode = (ScopedProxyMode) attribs.get("scopedProxy"); + if (scopedProxyMode != ScopedProxyMode.DEFAULT) { + scanner.setScopedProxyMode(scopedProxyMode); + } else { + scanner.setScopeMetadataResolver(BeanUtils.instantiateClass( + (Class)attribs.get("scopeResolver"), ScopeMetadataResolver.class)); + } + + scanner.setResourcePattern((String)attribs.get("resourcePattern")); + + for (Filter filter : (Filter[])attribs.get("includeFilters")) { + scanner.addIncludeFilter(createTypeFilter(filter)); + } + for (Filter filter : (Filter[])attribs.get("excludeFilters")) { + scanner.addExcludeFilter(createTypeFilter(filter)); + } + + List basePackages = new ArrayList(); + for (String pkg : (String[])attribs.get("value")) { + if (StringUtils.hasText(pkg)) { + basePackages.add(pkg); + } + } + for (String pkg : (String[])attribs.get("basePackages")) { + if (StringUtils.hasText(pkg)) { + basePackages.add(pkg); + } + } + for (Class clazz : (Class[])attribs.get("basePackageClasses")) { + // TODO: loading user types directly here. implications on load-time + // weaving may mean we need to revert to stringified class names in + // annotation metadata + basePackages.add(clazz.getPackage().getName()); + } + + if (basePackages.isEmpty()) { + throw new IllegalStateException("At least one base package must be specified"); + } + + scanner.scan(basePackages.toArray(new String[]{})); + } + + private TypeFilter createTypeFilter(Filter filter) { + switch (filter.type()) { + case ANNOTATION: + @SuppressWarnings("unchecked") + Class filterClass = (Class)filter.value(); + return new AnnotationTypeFilter(filterClass); + case ASSIGNABLE_TYPE: + return new AssignableTypeFilter(filter.value()); + case CUSTOM: + return BeanUtils.instantiateClass(filter.value(), TypeFilter.class); + default: + throw new IllegalArgumentException("unknown filter type " + filter.type()); + } + } +} diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java index 06e16297ade..570f37afa09 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java @@ -95,6 +95,8 @@ public class ConfigurationClassBeanDefinitionReader { private Environment environment; + private final ComponentScanAnnotationParser componentScanParser; + /** * Create a new {@link ConfigurationClassBeanDefinitionReader} instance that will be used * to populate the given {@link BeanDefinitionRegistry}. @@ -111,6 +113,8 @@ public class ConfigurationClassBeanDefinitionReader { this.metadataReaderFactory = metadataReaderFactory; this.resourceLoader = resourceLoader; this.environment = environment; + + this.componentScanParser = new ComponentScanAnnotationParser(resourceLoader, environment, registry); } @@ -130,6 +134,7 @@ public class ConfigurationClassBeanDefinitionReader { */ private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) { AnnotationMetadata metadata = configClass.getMetadata(); + componentScanParser.parse(metadata); doLoadBeanDefinitionForConfigurationClassIfNecessary(configClass); for (BeanMethod beanMethod : configClass.getBeanMethods()) { loadBeanDefinitionsForBeanMethod(beanMethod); diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationIntegrationTests.java b/org.springframework.context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationIntegrationTests.java index a418258fe2d..ab425604c3a 100644 --- a/org.springframework.context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationIntegrationTests.java +++ b/org.springframework.context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationIntegrationTests.java @@ -34,7 +34,6 @@ import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.annotation.CustomAutowireConfigurer; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.SimpleMapScope; -import org.springframework.beans.factory.parsing.BeanDefinitionParsingException; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.annotation.ComponentScan.Filter; @@ -114,7 +113,7 @@ public class ComponentScanAnnotationIntegrationTests { try { ctx.refresh(); fail("Expected exception when parsing @ComponentScan definition that declares no packages"); - } catch (BeanDefinitionParsingException ex) { + } catch (IllegalStateException ex) { assertThat(ex.getMessage(), containsString("At least one base package must be specified")); } }