当前位置:网站首页>@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 |

边栏推荐
- Notepad++ wrap by specified character
- "250000 a year is just the price of cabbage" has become a thing of the past. The annual salary of AI posts has decreased by 8.9%, and the latest salary report has been released
- Principles of BTC cryptography
- Method of finding prime number
- 獲取並監控遠程服務器日志
- [untitled]
- [escape character] [full of dry goods] super detailed explanation + code illustration!
- Transferring images using flask
- 穀歌 | 蛋白序列的深度嵌入和比對
- [set theory] relational closure (relational closure related theorem)
猜你喜欢

穀歌 | 蛋白序列的深度嵌入和比對
![Ensemble, série shuishu] jour 9](/img/39/c1ba1bac82b0ed110f36423263ffd0.png)
Ensemble, série shuishu] jour 9

Altaro o365 total backup subscription plan

Win10 install pytullet and test

Analysis of the example of network subnet division in secondary vocational school

2022.DAY592

谷歌 | 蛋白序列的深度嵌入和比对

redis 无法远程连接问题。

Principles of BTC cryptography

Why is go language particularly popular in China
随机推荐
Redis使用Lua脚本简介
@Autowired 导致空指针报错 解决方式
Best practices for setting up altaro VM backups
中职网络子网划分例题解析
【一起上水硕系列】Day 7 内容+Day8
Today, many CTOs were killed because they didn't achieve business
Altaro VM backup getting started
Linux登录MySQL出现ERROR 1045 (28000): Access denied for user ‘root‘@‘localhost‘ (using password: YES)
C 语言文件操作函数大全 (超详细)
"250000 a year is just the price of cabbage" has become a thing of the past. The annual salary of AI posts has decreased by 8.9%, and the latest salary report has been released
牛客网 JS 分隔符
【一起上水硕系列】Day 10
Common interview questions of microservice
How to use source insight
聊聊如何利用p6spy进行sql监控
Altaro set grandfather parent child (GFS) archiving
2022.7.2 simulation match
XML Configuration File
Classification and discussion of plane grab detection methods based on learning
獲取並監控遠程服務器日志