当前位置:网站首页>@Import annotation: four ways to import configuration classes & source code analysis
@Import annotation: four ways to import configuration classes & source code analysis
2022-07-03 05:39:00 【Manon stayup】

WeChat search : Code the agriculture StayUp
Home address :https://gozhuyinglong.github.io
The source code to share :https://github.com/gozhuyinglong/blog-demos
Small partners who usually like to see the source code , You should know Spring Used a lot of @Import annotation . The note is Spring Used to import configuration classes , Equivalent to Spring XML Medium <import/> Elements .
This article will introduce this annotation , Four ways of importing configuration classes are demonstrated by an example , Finally, analyze the source code of the annotation .
Don't talk much , Walk up ~
brief introduction
@Import The full class name of the annotation is org.springframework.context.annotation.Import. It has only one default value attribute , The attribute type is Class<?>[], Indicates that one or more... Can be passed in Class object .
It can be seen from the notes that , This annotation has the following functions :
- You can import one or more component classes ( Usually
@ConfigurationConfiguration class ) - The function of this annotation is similar to
Spring XMLMedium<import/>The elements are the same . You can import@ConfigurationConfiguration class 、ImportSelectandImportBeanDefinitionRegistrarImplementation class of . from 4.2 Version start , You can also reference general component classes ( General class ), This function is similar toAnnotationConfigApplicationContext.registerMethod . - This annotation can be declared in a class , You can also declare... In a meta annotation .
- If you need to import
XMLOr the other,@ConfigurationDefined resources , have access to@ImportResourcenotes .
There are four ways to import configuration classes
The source code comments are very clear , The annotation can be imported in four ways :
- General class
@ConfigurationConfiguration classImportSelectorImplementation class ofImportBeanDefinitionRegistrarImplementation class of
Now let's introduce one by one ~
preparation
Create four configuration classes :ConfigA、ConfigB、ConfigC、ConfigD. among ConfigB add @Configuration annotation , Represented as a configuration class , The other three are general .
ConfigA:
public class ConfigA {
public void print() {
System.out.println(" Output :ConfigA.class");
}
}
ConfigB:
@Configuration
public class ConfigB {
public void print() {
System.out.println(" Output :ConfigB.class");
}
}
ConfigC:
public class ConfigC {
public void print() {
System.out.println(" Output :ConfigC.class");
}
}
ConfigD:
public class ConfigD {
public void print() {
System.out.println(" Output :ConfigD.class");
}
}
Then create a main configuration class Config, And try to get through @Resource Annotation injects the above four configuration classes . Of course , This is not successful , You also need to import them .
@Configuration
public class Config {
@Resource
ConfigA configA;
@Resource
ConfigB configB;
@Resource
ConfigC configC;
@Resource
ConfigD configD;
public void print() {
configA.print();
configB.print();
configC.print();
configD.print();
}
}
Mode one : Import common classes
Importing a normal class is very simple , Just in @Import Of the incoming class Class Object can .
@Configuration
@Import(ConfigA.class)
public class Config {
...
}
Mode two : Import @Configuration Configuration class
Importing a configuration class is the same as importing a normal class , stay @Import Annotation passed into the target class Class object .
@Configuration
@Import({
ConfigA.class,
ConfigB.class})
public class Config {
...
}
Mode three : Import ImportSelector Implementation class of
ImportSelector The full class name of the interface is org.springframework.context.annotationImportSelector. Its main function is to collect the configuration classes that need to be imported , And determine which configuration classes need to be imported according to the conditions .
The implementation class of the interface can also implement any of the following Aware Interface , Their respective methods will be in selectImport Called before :
in addition , The interface implementation class can provide one or more constructors with the following formal parameter types :
If you want to delay importing configuration classes , Until all the @Configuration. Then you can use DeferredImportSelector
Let's create a class that implements this interface MyImportSelector.
Look at the following example :
stay selectImports In the method , Enter the reference AnnotationMetadata Main configuration class Config Annotation metadata .
The return value is the target configuration class ConfigC Full class name of , Here is an array , Indicates that multiple configuration classes can be imported .
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{
"io.github.gozhuyinglong.importanalysis.config.ConfigC"};
}
}
In the configuration class Config Import MyImportSelector class .
@Configuration
@Import({
ConfigA.class,
ConfigB.class,
MyImportSelector.class})
public class Config {
...
}
Mode 4 : Import ImportBeanDefinitionRegistrar Implementation class of
The purpose of this interface is to register selectively Bean, When registering, you can specify Bean name , And you can define bean The level of . Other functions and ImportSelector similar , No more details here .
Let's look at an example :
Create an implementation ImportBeanDefinitionRegistrar The class of the interface MyImportBeanDefinitionRegistrar, And in registerBeanDefinitions Method registration configD class .
Enter the reference AnnotationMetadata Main configuration class Config Annotation metadata ;BeanDefinitionRegistry Parameters can be registered Bean The definition information of .
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
registry.registerBeanDefinition("configD", new RootBeanDefinition(ConfigD.class));
}
}
In the configuration class Config Import MyImportBeanDefinitionRegistrar class .
@Configuration
@Import({
ConfigA.class,
ConfigB.class,
MyImportSelector.class,
MyImportBeanDefinitionRegistrar.class})
public class Config {
...
}
test result
Create a test class ImportDemo, See if the above four configuration classes are injected .
public class ImportDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
Config config = ctx.getBean(Config.class);
config.print();
}
}
Output results :
Output :ConfigA.class
Output :ConfigB.class
Output :ConfigC.class
Output :ConfigD.class
The output shows that , These four configuration classes are imported into the main configuration class , And successfully injected .
The source code parsing
ConfigurationClassParser Class is Spring Tool class of , It is mainly used to analyze configuration classes , And produce a set of ConfigurationClass object ( Because a configuration class may pass @Import Annotation to import other configuration classes ). in other words , It will recursively process all configuration classes .
doProcessConfigurationClass
Among them doProcessConfigurationClass Method is the process of handling all configuration classes , It is handled as follows :
- @Component annotation
- @PropertySource annotation
- @ComponentScan annotation
- @Import annotation
- @ImportResource annotation
- @Bean annotation
- Configure the default method on the interface of the class
- Super class of configuration class
@Nullable
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// 1. First, all member classes will be processed recursively , namely @Component annotation
processMemberClasses(configClass, sourceClass, filter);
}
// 2. Deal with all @PropertySource annotation
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// 3. Deal with all @ComponentScan annotation
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// The annotation of the configuration class is @ComponentScan-> Scan now
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the scanned BeanDefinition aggregate , See if there are other configuration classes , if necessary , Recursive parsing
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// 4. Deal with all @Import annotation
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
// 5. Deal with all @ImportResource annotation
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
// 6. Process marked as @Bean Method of annotation
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// 7. Handle the default methods on the interface of the configuration class
processInterfaces(configClass, sourceClass);
// 8. Handle the superclass of the configuration class ( If any )
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
return sourceClass.getSuperClass();
}
}
// Processing is complete
return null;
}
processImports
processImports The method is processing @Import Annotation imported configuration class , This is the theme of our article .
This method loops through each of the @Import Imported classes :
- ImportSelector Class
- ImportBeanDefinitionRegistrar Class
- Other classes are unified according to @Configuration Class to deal with , So add it or not @Configuration Annotations can be imported
/** * Handle... On configuration classes @Import Classes introduced by annotations * * @param configClass Configuration class , Here is Config class * @param currentSourceClass Current resource class * @param importCandidates In this configuration class @Import List of candidate classes for annotation import * @param exclusionFilter Remove the filter * @param checkForCircularImports Whether to cycle check the import */
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
// If it's time to @Import The list of imported annotations is empty , Go straight back to
if (importCandidates.isEmpty()) {
return;
}
// Loop check import
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
// Cycle through each by @Import Imported classes
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
// 1. ImportSelector Class
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
Predicate<String> selectorFilter = selector.getExclusionFilter();
if (selectorFilter != null) {
exclusionFilter = exclusionFilter.or(selectorFilter);
}
if (selector instanceof DeferredImportSelector) {
// 1.1 if DeferredImportSelector Interface implementation , Delay processing
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
// 1.2 Call our... Here ImportSelector Implementation class selectImports Method
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
// 1.3 Recursively process each selectImports Method
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// 2. ImportBeanDefinitionRegistrar Class
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// 3. Other classes are unified according to @Configuration Class to deal with , So add it or not @Configuration Annotations can be imported
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
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();
}
}
}
summary
Through the analysis of the above source code, we can see ,@Import Annotation is mainly used to import external classes , And ordinary classes will follow @Configuration Class to deal with . This greatly facilitates us to inject our component classes into the container ( There is no need to modify your own component class ).
The source code to share
Please visit my Github, If it helps you , Welcome to a , thank ~~
Recommended reading
- Java The reflex mechanism : Follow the code reflection
- JDK A dynamic proxy : Not only learn to use , More importantly, we should master its principle
About author
| project | Content |
|---|---|
| official account | Code the agriculture StayUp(ID:AcmenStayUp) |
| Home page | https://gozhuyinglong.github.io |
| CSDN | https://blog.csdn.net/gozhuyinglong |
| driving | https://juejin.cn/user/1239904849494856 |
| Github | https://github.com/gozhuyinglong |
| Gitee | https://gitee.com/gozhuyinglong |

边栏推荐
- Azure file synchronization of altaro: the end of traditional file servers?
- Final review (Day2)
- 获取并监控远程服务器日志
- 期末复习(DAY7)
- ROS Compilation Principle
- EMD distance - example of use
- [set theory] relational closure (relational closure related theorem)
- Jetson AGX Orin 平台移植ar0233-gw5200-max9295相机驱动
- Skip table: principle introduction, advantages and disadvantages of skiplist
- Transferring images using flask
猜你喜欢

"C and pointer" - Chapter 13 advanced pointer int * (* (* (*f) () [6]) ()

Intégration profonde et alignement des séquences de protéines Google

Why should we rewrite hashcode when we rewrite the equals method?

@Solutions to null pointer error caused by Autowired

一起上水硕系列】Day 9

@Autowired 导致空指针报错 解决方式

"C and pointer" - Chapter 13 function of function pointer 1 - callback function 1

Linux登录MySQL出现ERROR 1045 (28000): Access denied for user ‘root‘@‘localhost‘ (using password: YES)

Pessimistic lock and optimistic lock of multithreading

谷歌 | 蛋白序列的深度嵌入和比对
随机推荐
2022.7.2 模拟赛
期末复习DAY8
今天很多 CTO 都是被幹掉的,因為他沒有成就業務
Deploy crawl detection network using tensorrt (I)
Mapbox tasting value cloud animation
Altaro set grandfather parent child (GFS) archiving
Final review (day3)
chromedriver对应版本下载
2022.DAY592
Best practices for setting up altaro VM backups
求质数的方法
Obtenir et surveiller les journaux du serveur distant
Shanghai daoning, together with American /n software, will provide you with more powerful Internet enterprise communication and security component services
Niuke JS separator
Linux登录MySQL出现ERROR 1045 (28000): Access denied for user ‘root‘@‘localhost‘ (using password: YES)
一起上水碩系列】Day 9
Redis cannot connect remotely.
Principles of BTC cryptography
Can altaro back up Microsoft teams?
The IntelliJ platform completely disables the log4j component