@ -17,11 +17,12 @@
@@ -17,11 +17,12 @@
package org.springframework.context.annotation ;
import java.io.IOException ;
import java.lang.annotation.Annotation ;
import java.util.Arrays ;
import java.util.Collection ;
import java.util.Collections ;
import java.util.Comparator ;
import java.util.HashMap ;
import java.util.HashSet ;
import java.util.Iterator ;
import java.util.LinkedHashSet ;
import java.util.Map ;
@ -42,6 +43,7 @@ import org.springframework.beans.factory.support.BeanDefinitionReader;
@@ -42,6 +43,7 @@ import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.BeanDefinitionRegistry ;
import org.springframework.beans.factory.support.BeanNameGenerator ;
import org.springframework.context.ResourceLoaderAware ;
import org.springframework.core.NestedIOException ;
import org.springframework.core.annotation.AnnotationAttributes ;
import org.springframework.core.env.CompositePropertySource ;
import org.springframework.core.env.Environment ;
@ -54,7 +56,7 @@ import org.springframework.core.type.StandardAnnotationMetadata;
@@ -54,7 +56,7 @@ import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader ;
import org.springframework.core.type.classreading.MetadataReaderFactory ;
import org.springframework.core.type.filter.AssignableTypeFilter ;
import org.springframework.util.Collection Utils ;
import org.springframework.util.Object Utils ;
import org.springframework.util.StringUtils ;
import static org.springframework.context.annotation.MetadataUtils.* ;
@ -171,45 +173,12 @@ class ConfigurationClassParser {
@@ -171,45 +173,12 @@ class ConfigurationClassParser {
ConfigurationClass configClass , AnnotationMetadata metadata ) throws IOException {
// recursively process any member (nested) classes first
for ( String memberClassName : metadata . getMemberClassNames ( ) ) {
MetadataReader reader = this . metadataReaderFactory . getMetadataReader ( memberClassName ) ;
AnnotationMetadata memberClassMetadata = reader . getAnnotationMetadata ( ) ;
if ( ConfigurationClassUtils . isConfigurationCandidate ( memberClassMetadata ) ) {
processConfigurationClass ( new ConfigurationClass ( reader , true ) ) ;
}
}
processMemberClasses ( metadata ) ;
// process any @PropertySource annotations
AnnotationAttributes propertySource =
attributesFor ( metadata , org . springframework . context . annotation . PropertySource . class ) ;
AnnotationAttributes propertySource = attributesFor ( metadata , org . springframework . context . annotation . PropertySource . class ) ;
if ( propertySource ! = null ) {
String name = propertySource . getString ( "name" ) ;
String [ ] locations = propertySource . getStringArray ( "value" ) ;
int nLocations = locations . length ;
if ( nLocations = = 0 ) {
throw new IllegalArgumentException ( "At least one @PropertySource(value) location is required" ) ;
}
for ( int i = 0 ; i < nLocations ; i + + ) {
locations [ i ] = this . environment . resolveRequiredPlaceholders ( locations [ i ] ) ;
}
ClassLoader classLoader = this . resourceLoader . getClassLoader ( ) ;
if ( ! StringUtils . hasText ( name ) ) {
for ( String location : locations ) {
this . propertySources . push ( new ResourcePropertySource ( location , classLoader ) ) ;
}
}
else {
if ( nLocations = = 1 ) {
this . propertySources . push ( new ResourcePropertySource ( name , locations [ 0 ] , classLoader ) ) ;
}
else {
CompositePropertySource ps = new CompositePropertySource ( name ) ;
for ( String location : locations ) {
ps . addPropertySource ( new ResourcePropertySource ( location , classLoader ) ) ;
}
this . propertySources . push ( ps ) ;
}
}
processPropertySource ( propertySource ) ;
}
// process any @ComponentScan annotions
@ -228,9 +197,12 @@ class ConfigurationClassParser {
@@ -228,9 +197,12 @@ class ConfigurationClassParser {
}
// process any @Import annotations
Set < String > imports = getImports ( metadata . getClassName ( ) , null , new HashSet < String > ( ) ) ;
if ( ! CollectionUtils . isEmpty ( imports ) ) {
processImport ( configClass , imports . toArray ( new String [ imports . size ( ) ] ) , true ) ;
Set < Object > imports = new LinkedHashSet < Object > ( ) ;
Set < Object > visited = new LinkedHashSet < Object > ( ) ;
collectImports ( metadata , imports , visited ) ;
if ( ! imports . isEmpty ( ) ) {
processImport ( configClass , imports , true ) ;
}
// process any @ImportResource annotations
@ -279,6 +251,65 @@ class ConfigurationClassParser {
@@ -279,6 +251,65 @@ class ConfigurationClassParser {
return null ;
}
/ * *
* Register member ( nested ) classes that happen to be configuration classes themselves .
* @param metadata the metadata representation of the containing class
* @throws IOException if there is any problem reading metadata from a member class
* /
private void processMemberClasses ( AnnotationMetadata metadata ) throws IOException {
if ( metadata instanceof StandardAnnotationMetadata ) {
for ( Class < ? > memberClass : ( ( StandardAnnotationMetadata ) metadata ) . getIntrospectedClass ( ) . getDeclaredClasses ( ) ) {
if ( ConfigurationClassUtils . isConfigurationCandidate ( new StandardAnnotationMetadata ( memberClass ) ) ) {
processConfigurationClass ( new ConfigurationClass ( memberClass , true ) ) ;
}
}
}
else {
for ( String memberClassName : metadata . getMemberClassNames ( ) ) {
MetadataReader reader = this . metadataReaderFactory . getMetadataReader ( memberClassName ) ;
AnnotationMetadata memberClassMetadata = reader . getAnnotationMetadata ( ) ;
if ( ConfigurationClassUtils . isConfigurationCandidate ( memberClassMetadata ) ) {
processConfigurationClass ( new ConfigurationClass ( reader , true ) ) ;
}
}
}
}
/ * *
* Process the given < code > @PropertySource < / code > annotation metadata .
* @param propertySource metadata for the < code > @PropertySource < / code > annotation found
* @throws IOException if loading a property source failed
* /
private void processPropertySource ( AnnotationAttributes propertySource ) throws IOException {
String name = propertySource . getString ( "name" ) ;
String [ ] locations = propertySource . getStringArray ( "value" ) ;
int nLocations = locations . length ;
if ( nLocations = = 0 ) {
throw new IllegalArgumentException ( "At least one @PropertySource(value) location is required" ) ;
}
for ( int i = 0 ; i < nLocations ; i + + ) {
locations [ i ] = this . environment . resolveRequiredPlaceholders ( locations [ i ] ) ;
}
ClassLoader classLoader = this . resourceLoader . getClassLoader ( ) ;
if ( ! StringUtils . hasText ( name ) ) {
for ( String location : locations ) {
this . propertySources . push ( new ResourcePropertySource ( location , classLoader ) ) ;
}
}
else {
if ( nLocations = = 1 ) {
this . propertySources . push ( new ResourcePropertySource ( name , locations [ 0 ] , classLoader ) ) ;
}
else {
CompositePropertySource ps = new CompositePropertySource ( name ) ;
for ( String location : locations ) {
ps . addPropertySource ( new ResourcePropertySource ( location , classLoader ) ) ;
}
this . propertySources . push ( ps ) ;
}
}
}
/ * *
* Recursively collect all declared { @code @Import } values . Unlike most
* meta - annotations it is valid to have several { @code @Import } s declared with
@ -287,69 +318,99 @@ class ConfigurationClassParser {
@@ -287,69 +318,99 @@ class ConfigurationClassParser {
* < p > For example , it is common for a { @code @Configuration } class to declare direct
* { @code @Import } s in addition to meta - imports originating from an { @code @Enable }
* annotation .
* @param className the class name to search
* @param imports the imports collected so far or { @code null }
* @param visited used to track visited classes to prevent infinite recursion ( must not be null )
* @return a set of all { @link Import # value ( ) import values } or { @code null }
* @param metadata the metadata representation of the class to search
* @param imports the imports collected so far
* @param visited used to track visited classes to prevent infinite recursion
* @throws IOException if there is any problem reading metadata from the named class
* /
private Set < String > getImports ( String className , Set < String > imports , Set < String > visited ) throws IOException {
if ( visited . add ( className ) & & ! className . startsWith ( "java" ) ) {
AnnotationMetadata metadata = metadataReaderFactory . getMetadataReader ( className ) . getAnnotationMetadata ( ) ;
for ( String annotationType : metadata . getAnnotationTypes ( ) ) {
imports = getImports ( annotationType , imports , visited ) ;
private void collectImports ( AnnotationMetadata metadata , Set < Object > imports , Set < Object > visited ) throws IOException {
String className = metadata . getClassName ( ) ;
if ( visited . add ( className ) ) {
if ( metadata instanceof StandardAnnotationMetadata ) {
StandardAnnotationMetadata stdMetadata = ( StandardAnnotationMetadata ) metadata ;
for ( Annotation ann : stdMetadata . getIntrospectedClass ( ) . getAnnotations ( ) ) {
if ( ! ann . annotationType ( ) . getName ( ) . startsWith ( "java" ) & & ! ( ann instanceof Import ) ) {
collectImports ( new StandardAnnotationMetadata ( ann . annotationType ( ) ) , imports , visited ) ;
}
}
Map < String , Object > attributes = stdMetadata . getAnnotationAttributes ( Import . class . getName ( ) , false ) ;
if ( attributes ! = null ) {
Class [ ] value = ( Class [ ] ) attributes . get ( "value" ) ;
if ( ! ObjectUtils . isEmpty ( value ) ) {
imports . addAll ( Arrays . asList ( value ) ) ;
}
}
}
Map < String , Object > attributes = metadata . getAnnotationAttributes ( Import . class . getName ( ) , true ) ;
if ( attributes ! = null ) {
String [ ] value = ( String [ ] ) attributes . get ( "value" ) ;
if ( value ! = null & & value . length > 0 ) {
imports = ( imports = = null ? new LinkedHashSet < String > ( ) : imports ) ;
imports . addAll ( Arrays . asList ( value ) ) ;
else {
for ( String annotationType : metadata . getAnnotationTypes ( ) ) {
if ( ! className . startsWith ( "java" ) & & ! className . equals ( Import . class . getName ( ) ) ) {
try {
collectImports (
new StandardAnnotationMetadata ( this . resourceLoader . getClassLoader ( ) . loadClass ( annotationType ) ) ,
imports , visited ) ;
}
catch ( ClassNotFoundException ex ) {
//
}
}
}
Map < String , Object > attributes = metadata . getAnnotationAttributes ( Import . class . getName ( ) , true ) ;
if ( attributes ! = null ) {
String [ ] value = ( String [ ] ) attributes . get ( "value" ) ;
if ( ! ObjectUtils . isEmpty ( value ) ) {
imports . addAll ( Arrays . asList ( value ) ) ;
}
}
}
}
return imports ;
}
private void processImport ( ConfigurationClass configClass , String [ ] classesToImport , boolean checkForCircularImports ) throws IOException {
private void processImport ( ConfigurationClass configClass , Collection < ? > classesToImport , boolean checkForCircularImports ) throws IOException {
if ( checkForCircularImports & & this . importStack . contains ( configClass ) ) {
this . problemReporter . error ( new CircularImportProblem ( configClass , this . importStack , configClass . getMetadata ( ) ) ) ;
}
else {
this . importStack . push ( configClass ) ;
AnnotationMetadata importingClassMetadata = configClass . getMetadata ( ) ;
for ( String candidate : classesToImport ) {
MetadataReader reader = this . metadataReaderFactory . getMetadataReader ( candidate ) ;
if ( new AssignableTypeFilter ( ImportSelector . class ) . match ( reader , this . metadataReaderFactory ) ) {
// the candidate class is an ImportSelector -> delegate to it to determine imports
try {
ImportSelector selector = BeanUtils . instantiateClass (
this . resourceLoader . getClassLoader ( ) . loadClass ( candidate ) , ImportSelector . class ) ;
processImport ( configClass , selector . selectImports ( importingClassMetadata ) , false ) ;
try {
for ( Object candidate : classesToImport ) {
Object candidateToCheck = ( candidate instanceof Class ? ( Class ) candidate : this . metadataReaderFactory . getMetadataReader ( ( String ) candidate ) ) ;
if ( checkAssignability ( ImportSelector . class , candidateToCheck ) ) {
// the candidate class is an ImportSelector -> delegate to it to determine imports
Class < ? > candidateClass = ( candidate instanceof Class ? ( Class ) candidate : this . resourceLoader . getClassLoader ( ) . loadClass ( ( String ) candidate ) ) ;
ImportSelector selector = BeanUtils . instantiateClass ( candidateClass , ImportSelector . class ) ;
processImport ( configClass , Arrays . asList ( selector . selectImports ( importingClassMetadata ) ) , false ) ;
}
catch ( ClassNotFoundException ex ) {
throw new IllegalStateException ( ex ) ;
}
}
else if ( new AssignableTypeFilter ( ImportBeanDefinitionRegistrar . class ) . match ( reader , this . metadataReaderFactory ) ) {
// the candidate class is an ImportBeanDefinitionRegistrar -> delegate to it to register additional bean definitions
try {
ImportBeanDefinitionRegistrar registrar = BeanUtils . instantiateClass (
this . resourceLoader . getClassLoader ( ) . loadClass ( candidate ) , ImportBeanDefinitionRegistrar . class ) ;
else if ( checkAssignability ( ImportBeanDefinitionRegistrar . class , candidateToCheck ) ) {
// the candidate class is an ImportBeanDefinitionRegistrar -> delegate to it to register additional bean definitions
Class < ? > candidateClass = ( candidate instanceof Class ? ( Class ) candidate : this . resourceLoader . getClassLoader ( ) . loadClass ( ( String ) candidate ) ) ;
ImportBeanDefinitionRegistrar registrar = BeanUtils . instantiateClass ( candidateClass , ImportBeanDefinitionRegistrar . class ) ;
invokeAwareMethods ( registrar ) ;
registrar . registerBeanDefinitions ( importingClassMetadata , registry ) ;
registrar . registerBeanDefinitions ( importingClassMetadata , this . registry ) ;
}
catch ( ClassNotFoundException ex ) {
throw new IllegalStateException ( ex ) ;
else {
// candidate class not an ImportSelector or ImportBeanDefinitionRegistrar -> process it as a @Configuration class
this . importStack . registerImport ( importingClassMetadata . getClassName ( ) , ( candidate instanceof Class ? ( ( Class ) candidate ) . getName ( ) : ( String ) candidate ) ) ;
processConfigurationClass ( candidateToCheck instanceof Class ? new ConfigurationClass ( ( Class ) candidateToCheck , true ) :
new ConfigurationClass ( ( MetadataReader ) candidateToCheck , true ) ) ;
}
}
else {
// the candidate class not an ImportSelector or ImportBeanDefinitionRegistrar -> process it as a @Configuration class
this . importStack . registerImport ( importingClassMetadata . getClassName ( ) , candidate ) ;
processConfigurationClass ( new ConfigurationClass ( reader , true ) ) ;
}
}
this . importStack . pop ( ) ;
catch ( ClassNotFoundException ex ) {
throw new NestedIOException ( "Failed to load import candidate class" , ex ) ;
}
finally {
this . importStack . pop ( ) ;
}
}
}
private boolean checkAssignability ( Class < ? > clazz , Object candidate ) throws IOException {
if ( candidate instanceof Class ) {
return clazz . isAssignableFrom ( ( Class ) candidate ) ;
}
else {
return new AssignableTypeFilter ( clazz ) . match ( ( MetadataReader ) candidate , this . metadataReaderFactory ) ;
}
}