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
This commit is contained in:
parent
94aa90646b
commit
845f4f2bb7
|
|
@ -23,6 +23,7 @@ import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||||
|
import org.springframework.core.annotation.AliasFor;
|
||||||
import org.springframework.core.type.filter.TypeFilter;
|
import org.springframework.core.type.filter.TypeFilter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -46,6 +47,7 @@ import org.springframework.core.type.filter.TypeFilter;
|
||||||
*
|
*
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
|
* @author Sam Brannen
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
* @see Configuration
|
* @see Configuration
|
||||||
*/
|
*/
|
||||||
|
|
@ -60,6 +62,7 @@ public @interface ComponentScan {
|
||||||
* are needed — for example, {@code @ComponentScan("org.my.pkg")}
|
* are needed — for example, {@code @ComponentScan("org.my.pkg")}
|
||||||
* instead of {@code @ComponentScan(basePackages = "org.my.pkg")}.
|
* instead of {@code @ComponentScan(basePackages = "org.my.pkg")}.
|
||||||
*/
|
*/
|
||||||
|
@AliasFor(attribute = "basePackages")
|
||||||
String[] value() default {};
|
String[] value() default {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -69,6 +72,7 @@ public @interface ComponentScan {
|
||||||
* <p>Use {@link #basePackageClasses} for a type-safe alternative to
|
* <p>Use {@link #basePackageClasses} for a type-safe alternative to
|
||||||
* String-based package names.
|
* String-based package names.
|
||||||
*/
|
*/
|
||||||
|
@AliasFor(attribute = "value")
|
||||||
String[] basePackages() default {};
|
String[] basePackages() default {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
import org.springframework.core.annotation.AnnotationAttributes;
|
import org.springframework.core.annotation.AnnotationAttributes;
|
||||||
|
import org.springframework.core.annotation.AnnotationConfigurationException;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.core.io.ResourceLoader;
|
import org.springframework.core.io.ResourceLoader;
|
||||||
import org.springframework.core.type.filter.AbstractTypeHierarchyTraversingFilter;
|
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.core.type.filter.TypeFilter;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
import org.springframework.util.ObjectUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -115,11 +117,7 @@ class ComponentScanAnnotationParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<String> basePackages = new LinkedHashSet<String>();
|
Set<String> basePackages = new LinkedHashSet<String>();
|
||||||
Set<String> specifiedPackages = new LinkedHashSet<String>();
|
for (String pkg : getBasePackages(componentScan, declaringClass)) {
|
||||||
specifiedPackages.addAll(Arrays.asList(componentScan.getStringArray("value")));
|
|
||||||
specifiedPackages.addAll(Arrays.asList(componentScan.getStringArray("basePackages")));
|
|
||||||
|
|
||||||
for (String pkg : specifiedPackages) {
|
|
||||||
String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
|
String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
|
||||||
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
|
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
|
||||||
basePackages.addAll(Arrays.asList(tokenized));
|
basePackages.addAll(Arrays.asList(tokenized));
|
||||||
|
|
@ -140,6 +138,25 @@ class ComponentScanAnnotationParser {
|
||||||
return scanner.doScan(StringUtils.toStringArray(basePackages));
|
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<TypeFilter> typeFiltersFor(AnnotationAttributes filterAttributes) {
|
private List<TypeFilter> typeFiltersFor(AnnotationAttributes filterAttributes) {
|
||||||
List<TypeFilter> typeFilters = new ArrayList<TypeFilter>();
|
List<TypeFilter> typeFilters = new ArrayList<TypeFilter>();
|
||||||
FilterType filterType = filterAttributes.getEnum("type");
|
FilterType filterType = filterAttributes.getEnum("type");
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue