From 845f4f2bb7791f4c759e06b7f22109e33ba91b5d Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Sun, 31 May 2015 15:37:31 +0200 Subject: [PATCH] Improve attribute alias support in @ComponentScan Prior to this commit, @ComponentScan already had a value/basePackages alias pair; however, the semantics were not properly enforced. This commit addresses this issue by refactoring ComponentScanAnnotationParser to ensure that it is not possible to declare both of the aliased attributes. In addition, the 'value' and 'basePackages' attributes are now annotated with @AliasFor in order to make the semantics clearer. Issue: SPR-11393 --- .../context/annotation/ComponentScan.java | 4 +++ .../ComponentScanAnnotationParser.java | 27 +++++++++++++++---- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScan.java b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScan.java index 6f757e316f0..9a9e85ce5ff 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScan.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScan.java @@ -23,6 +23,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.beans.factory.support.BeanNameGenerator; +import org.springframework.core.annotation.AliasFor; import org.springframework.core.type.filter.TypeFilter; /** @@ -46,6 +47,7 @@ import org.springframework.core.type.filter.TypeFilter; * * @author Chris Beams * @author Juergen Hoeller + * @author Sam Brannen * @since 3.1 * @see Configuration */ @@ -60,6 +62,7 @@ public @interface ComponentScan { * are needed — for example, {@code @ComponentScan("org.my.pkg")} * instead of {@code @ComponentScan(basePackages = "org.my.pkg")}. */ + @AliasFor(attribute = "basePackages") String[] value() default {}; /** @@ -69,6 +72,7 @@ public @interface ComponentScan { *

Use {@link #basePackageClasses} for a type-safe alternative to * String-based package names. */ + @AliasFor(attribute = "value") String[] basePackages() default {}; /** diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java index 7b0ef1c17ce..b07f67901bf 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java @@ -30,6 +30,7 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.core.annotation.AnnotationConfigurationException; import org.springframework.core.env.Environment; import org.springframework.core.io.ResourceLoader; import org.springframework.core.type.filter.AbstractTypeHierarchyTraversingFilter; @@ -40,6 +41,7 @@ import org.springframework.core.type.filter.RegexPatternTypeFilter; import org.springframework.core.type.filter.TypeFilter; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; +import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; /** @@ -115,11 +117,7 @@ class ComponentScanAnnotationParser { } Set basePackages = new LinkedHashSet(); - Set specifiedPackages = new LinkedHashSet(); - specifiedPackages.addAll(Arrays.asList(componentScan.getStringArray("value"))); - specifiedPackages.addAll(Arrays.asList(componentScan.getStringArray("basePackages"))); - - for (String pkg : specifiedPackages) { + for (String pkg : getBasePackages(componentScan, declaringClass)) { String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg), ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); basePackages.addAll(Arrays.asList(tokenized)); @@ -140,6 +138,25 @@ class ComponentScanAnnotationParser { return scanner.doScan(StringUtils.toStringArray(basePackages)); } + private String[] getBasePackages(AnnotationAttributes componentScan, String declaringClass) { + String[] value = componentScan.getStringArray("value"); + String[] basePackages = componentScan.getStringArray("basePackages"); + boolean valueDeclared = !ObjectUtils.isEmpty(value); + boolean basePackagesDeclared = !ObjectUtils.isEmpty(basePackages); + + if (valueDeclared && basePackagesDeclared && !ObjectUtils.nullSafeEquals(value, basePackages)) { + String msg = String.format("In @ComponentScan declared on [%s], attribute [value] " + + "and its alias [basePackages] are present with values of [%s] and [%s], " + + "but only one is permitted.", declaringClass, ObjectUtils.nullSafeToString(value), + ObjectUtils.nullSafeToString(basePackages)); + throw new AnnotationConfigurationException(msg); + } + if (!basePackagesDeclared) { + basePackages = value; + } + return basePackages; + } + private List typeFiltersFor(AnnotationAttributes filterAttributes) { List typeFilters = new ArrayList(); FilterType filterType = filterAttributes.getEnum("type");