Spring refresh - invokeBeanFactoryPostProcessors - resolve @Import
The @Import annotation brings a lot of flexibility to Spring bean creation, which greatly simplifies the use of Spring because of its encapsulation of configuration. @Enable* in Spring is basically implemented by @Import (add @Import annotation on @Enable to import other configuration classes)
How to use @Import
Usually use @Import on a configuration class to import other configuration classes
- Annotated with @Configuration, marked as FULL configuration class
- There is no @Configuration annotation, but there is @Component, @ComponentScan, @Import, @ImportResource, or a method annotated with @Bean annotation in the class, marked as a LITE configuration class
Three ways to use @Import
- Import a configuration class directly (normal classes can also be imported after 4.2)
- Import the implementation class of the ImportSelector interface, parse a set of classes returned by the selectImports method as BeanDefinition and register
- Import the implementation class of the ImportBeanDefinitionRegistrar interface, build and register the BeanDefinition by yourself
Which @Imports can be resolved
- @Import on the configuration class
- @Import inside other annotations on the configuration class, and @Import inside [email protected] annotations recursively from the configuration class
If the @Import being scanned imports a non-configured class, but there is an @Import on that class, it will also be resolved to
Note that classes imported by @Import will not be registered as BeanDefinitions and will not produce singleton beans
import configuration class
@EnableWebMvc
@Retention ( RetentionPolicy . RUNTIME )
@Target ( ElementType . TYPE )
@Documented
@Import ( DelegatingWebMvcConfiguration . class )
public @ interface EnableWebMvc { }
- 1
- 2
- 3
- 4
- 5
ImportSelector
@EnableAsync
@Target ( ElementType . TYPE )
@Retention ( RetentionPolicy . RUNTIME )
@Documented
@Import ( AsyncConfigurationSelector . class )
public @ interface EnableAsync {
Class < ? extends Annotation > annotation ( ) default Annotation . class ;
boolean proxyTargetClass ( ) default false ;
AdviceModemode ( ) default AdviceMode . PROXY ;
int order ( ) default Ordered . LOWEST_PRECEDENCE ;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
public class AsyncConfigurationSelector extends AdviceModeImportSelector < EnableAsync > {
private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration" ;
@Override
public String [ ] selectImports ( AdviceMode adviceMode ) {
switch ( adviceMode ) {
case PROXY :
return new String [ ] {ProxyAsyncConfiguration . class . getName ( ) } ;
case ASPECTJ :
return new String [ ] { ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME } ;
default :
return null ;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
The main function of the selectImports method of ImportSelector is to collect the configuration classes that need to be imported. A set of class-qualified names returned by the method will be parsed as configuration classes (walking through the complete process of parsing configuration classes will parse @PropertySource, @ComponentScan on this class) , @Import, @ImportSource, @Bean methods, etc.), ImportSelector itself is not registered as a BeanDefinition
Before calling the selectImports method of the ImportSelector, if the imported ImportSelector also implements EnvironmentAware, BeanFactoryAware, BeanClassLoaderAware, ResourceLoaderAware, then these will be taken and set to the corresponding properties of the ImportSelector
DeferredImportSelector delay import
@EnableAutoConfiguration
@Target ( { ElementType . TYPE } )
@Retention ( RetentionPolicy . RUNTIME )
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan ( excludeFilters = {
@Filter ( type = FilterType . CUSTOM , classes = { TypeExcludeFilter . class } ) ,
@Filter ( type = FilterType .CUSTOM , classes = { AutoConfigurationExcludeFilter . class } ) }
)
public @interface SpringBootApplication { } _
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
@Target ( { ElementType . TYPE } )
@Retention ( RetentionPolicy . RUNTIME )
@Documented
@Inherited
@AutoConfigurationPackage
@Import ( { AutoConfigurationImportSelector . class } )
public @ interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration" ;
Class < ? > [ ] exclude ( ) default { } ;
String [ ] excludeName ( ) default { } ;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
public class AutoConfigurationImportSelector implements DeferredImportSelector , BeanClassLoaderAware , ResourceLoaderAware , BeanFactoryAware , EnvironmentAware , Ordered {
// ...
public Class < ? extends Group > getImportGroup ( ) {
return AutoConfigurationImportSelector . AutoConfigurationGroup . class ;
}
// ...
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
The DeferredImportSelector interface is a sub-interface of the ImportSelector interface. The configuration class introduced by this interface will parse other annotations and configuration classes in Spring (including but not limited to the configuration class introduced by ImportSelector, excluding the automatic configuration class, that is, the configuration class in the spring.factories file. ) and then parsed, depending on this design, the automatic configuration of SpringBoot can be realized. When the automatic configuration class is parsed, it will judge (@Conditional) whether the user-configured bean already exists in the Spring container according to the conditions. If it exists, the automatic configuration will not be executed. . This interface can also be used with the interface Ordered or @Order to define the priority of multiple DeferredImportSelector selectors
It also provides grouping function. First call the getImportGroup method of DeferredImportSelector to determine whether there is a custom group. If there is one, call the selectImports method of the group. If not, call the selectImports method of DeferredImportSelector. The selectImports method can be sorted
ImportBeanDefinitionRegistrar
@EnableAspectJAutoProxy
@Target ( ElementType . TYPE )
@Retention ( RetentionPolicy . RUNTIME )
@Documented
@Import ( AspectJAutoProxyRegistrar . class )
public @ interface EnableAspectJAutoProxy {
boolean proxyTargetClass ( ) default false ;
boolean exposeProxy ( ) default false ;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions (
AnnotationMetadata importingClassMetadata , BeanDefinitionRegistry registry ) {
AopConfigUtils . registerAspectJAnnotationAutoProxyCreatorIfNecessary ( registry ) ;
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils . attributesFor ( importingClassMetadata , EnableAspectJAutoProxy .class ) ;
if ( enableAspectJAutoProxy . getBoolean ( "proxyTargetClass" ) ) {
AopConfigUtils . forceAutoProxyCreatorToUseClassProxying ( registry ) ;
}
if ( enableAspectJAutoProxy . getBoolean ( "exposeProxy" ) ) {
AopConfigUtils . forceAutoProxyCreatorToExposeProxy ( registry ) ;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
The ImportBeanDefinitionRegistrar interface provides two default implementations of the registerBeanDefinitions method, which can be overridden to customize BeanDefinitions and register
Process of parsing @Import
- Starting from the current configuration class, recursively search for @Import annotations on the class, as well as @Import annotations in [email protected] annotations on the class (the annotation class names starting with java are filtered out). Get all the classes imported by the @Import annotation. For example The configuration class is currently parsed, @Import(A.class) on the configuration class, @Import(B.class) on A, and an @Xxxxx annotation on the configuration class. In this annotation @Import(C.class), finally What I got is A.class, C.class
- Traverse the imported classes obtained
- If the imported class is ImportSelector (import selector: used to select the class to be imported)
- If the imported class is deferred import DeferredImportSelector (deferred import selector: used to select the class to be imported, the resolution of the imported class will be delayed), the imported selector will be stored in the deferredImportSelectors of ConfigurationClassParser. Where to deal with later
- If the imported class is not a lazy import, recursively import the imported class and then the imported class. At the end of the recursive parsing, the class that is neither ImportSelector nor ImportBeanDefinitionRegistrar will be reached
- If the imported class is ImportBeanDefinitionRegistrar (Imported BeanDefinition Registrar: used to register the BeanDefinition that needs to be imported), add this ImportBeanDefinitionRegistrar to the importBeanDefinitionRegistrars of configClass. In the ConfigurationClassBeanDefinitionReader#loadBeanDefinitions process after parse, its method is traversed and called to register the BeanDefinition
- If the imported class is neither ImportSelector nor ImportBeanDefinitionRegistrar, parse it as a configuration class, and follow the same process of parsing @Configuration. Instead of generating and registering BeanDefinition directly, it is first saved in the import of importStack of ConfigurationClassParser
- If the imported class is ImportSelector (import selector: used to select the class to be imported)
Detailed process description
ConfigurationClassPostProcessor#processConfigBeanDefinitions
public void processConfigBeanDefinitions ( BeanDefinitionRegistry registry ) {
List < BeanDefinitionHolder > configCandidates = new ArrayList < > ( ) ;
// ...
// Start parsing the configuration class
// Parse the @PropertySource, @ComponentScan, @Import, @ImportResource, @Bean methods of the
configuration class one by one Parsing
// @Import imported classes will be divided into 3 types,
// Delayed in ImportSelector is temporarily stored, non-delayed is recursively parsed,
// ImportBeanDefinitionRegistrar is temporarily stored,
// None will be treated as configuration classes Go through the configuration class parsing process
// The xml imported by @ImportResource is also temporarily stored
// The methods annotated by @Bean are also temporarily stored
parser . parse ( candidates ) ;
// Process all the above temporarily stored in a unified way
// Get the parsed configuration class
Set in the parser < ConfigurationClass > configClasses = new LinkedHashSet < > ( parser . getConfigurationClasses ( ) ) ;
configClasses . removeAll ( alreadyParsed ) ;
// Read the model and create bean definitions based on its content
if ( this . reader == null ) {
this . reader = new ConfigurationClassBeanDefinitionReader (
registry , this . sourceExtractor , this . resourceLoader , this . environment ,
this . importBeanNameGenerator , parser . getImportRegistry ( ) ) ;
}
// Read and register the BeanDefinition from the parsed configuration class with a reader
// ImportBeanDefinitionRegistrar's subsequent registration BeanDefinition is here
// the subsequent processing of DeferredImportSelector has not found
this . reader . loadBeanDefinitions ( configClasses ) ;
// ...
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
ConfigurationClassParser#doProcessConfigurationClass parses the @Import section
// Process any @Import annotations
// Process @Import annotation
processImports ( configClass , sourceClass , getImports ( sourceClass ) , filter , true ) ;
- 1
- 2
- 3
ConfigurationClassParser#getImports
Starting from the current configuration class, recursively search for @Import annotations on the class, as well as @Import annotations in [email protected] annotations on the class (the annotation class names starting with java are filtered out). Get all the classes imported by the @Import annotation. For example The configuration class is currently parsed, @Import(A.class) on the configuration class, @Import(B.class) on A, and an @Xxxxx annotation on the configuration class. In this annotation @Import(C.class), finally What I got is A.class, C.class
private Set < SourceClass > getImports ( SourceClass sourceClass ) throws IOException {
Set < SourceClass > imports = new LinkedHashSet < > ( ) ;
Set < SourceClass > visited = new LinkedHashSet < > ( ) ;
collectImports ( sourceClass , imports , visited ) ;
returnimports ;
}
- 1
- 2
- 3
- 4
- 5
- 6
private void collectImports ( SourceClass sourceClass , Set < SourceClass > imports , Set < SourceClass > visited )
throws IOException {
// Get the imported class of the @Import annotation on sourceClass,
// and the imported class of the @Import annotation introduced in other annotations on sourceClass,
// and the @Import annotation introduced in other annotations in other annotations on sourceClass The imported class
// and the imported class of the @Import annotation introduced in other annotations in other annotations on sourceClass
//...
// Recursive layer by layer
if ( visited . add ( sourceClass ) ) {
for ( SourceClass annotation : sourceClass . getAnnotations ( ) ) {
String annName = annotation . getMetadata ( ) . getClassName ( );
if ( ! annName . equals ( Import . class . getName ( ) ) ) {
// Recursively current method
collectImports ( annotation , imports , visited ) ;
}
}
imports . addAll ( sourceClass . getAnnotationAttributes ( Import . class . getName ( ) , "value" ) ) ;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
ConfigurationClassParser#processImports
private void processImports ( ConfigurationClass configClass , SourceClass currentSourceClass ,
Collection < SourceClass > importCandidates , Predicate < String > exclusionFilter ,
boolean checkForCircularImports ) {
if ( importCandidates . isEmpty ( ) ) {
return ;
}
if ( checkForCircularImports && isChainedImportOnStack ( configClass ) ) {
this . problemReporter . error ( new CircularImportProblem ( configClass , this . importStack ) ) ;
}
else {
this . importStack . push ( configClass ) ;
try {
for ( SourceClass candidate : importCandidates ) {
// What is imported is ImportSelector (import selector: used to select the class to be imported)
// The judgment is whether the candidate component class is ImportSelector or whether the candidate component class is the parent class of ImportSelector
if ( candidate . isAssignable ( ImportSelector . class ) ) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class < ? > candidateClass = candidate . loadClass ( ) ;
// Instantiate this ImportSelector and inject 4 Aware
ImportSelector selectors if necessary = ParserStrategyUtils . instantiateClass ( candidateClass , ImportSelector . class ,
this . environment , this . resourceLoader , this . registry ) ;
Predicate < String > selectorFilter = selector . getExclusionFilter ( ) ;
if ( selectorFilter != null ) {
exclusionFilter = exclusionFilter . or ( selectorFilter ) ;
}
// If it is a delayed import of DeferredImportSelector (deferred import selector: used to select the class to be imported, the parsing of the imported class will be delayed)
// The imported selector will be stored in the deferredImportSelectors of ConfigurationClassParser
if ( selector instanceof DeferredImportSelector ) {
this . deferredImportSelectorHandler . handle ( configClass , ( DeferredImportSelector ) selector ) ;
}
// not lazy import
else {
String [ ] importClassNames = selector . selectImports ( currentSourceClass . getMetadata ( ) ) ;
Collection < SourceClass > importSourceClasses = asSourceClasses ( importClassNames , exclusionFilter ) ;
// Recursively import the imported class of this class and then the imported class
// The last of the recursive analysis must be a class that is neither ImportSelector nor ImportBeanDefinitionRegistrar
processImports ( configClass , currentSourceClass , importSourceClasses , exclusionFilter , false ) ;
}
}
// ImportBeanDefinitionRegistrar (Imported BeanDefinition Registrar: used to register BeanDefinitions that need to be imported)
else if ( candidate . isAssignable ( ImportBeanDefinitionRegistrar . class ) ) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class < ? > candidateClass = candidate . loadClass ( ) ;
// Instantiate this ImportBeanDefinitionRegistrar and inject 4 Aware if necessary
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils . instantiateClass ( candidateClass , ImportBeanDefinitionRegistrar . class ,
this . environment , this . resourceLoader , this . registry ) ;
// Add this ImportBeanDefinitionRegistrar to the importBeanDefinitionRegistrars of configClass
configClass . addImportBeanDefinitionRegistrar ( registrar , currentSourceClass . getMetadata ( ) ) ;
}
// The imported one is neither ImportSelector nor ImportBeanDefinitionRegistrar
// Parse it as a configuration class, and follow the same process of parsing @Configuration
// The BeanDefinition is not directly generated and registered, but is first saved in the import of the importStack of the ConfigurationClassParser
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
System . out . println ( this . importStack . imports ) ;
// Add the imported class information to the import of the importStack of the ConfigurationClassParser
this . importStack . registerImport (
currentSourceClass . getMetadata ( ) , candidate . getMetadata ( ) . getClassName ( ) ) ;
System . out . println ( this . importStack . imports ) ;
// Parse the imported class as a configuration class, follow the same process of parsing @Configuration
processConfigurationClass ( candidate . asConfigClass ( configClass ) , exclusionFilter ) ;
}
}
}
catch ( BeanDefinitionStoreException ex ) {
throw ex ;
}
catch ( Throwable ex ) {
throw new BeanDefinitionStoreException (
"Failed to process import candidates for configuration class [" +
configClass .getMetadata ( ) . getClassName ( ) + "]" , ex ) ;
}
finally {
this . importStack . pop ( ) ;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94