Spring注解之@Import使用方法講解
1. 前言
Spring提供了@Import注解,用于向容器引入我們自定義的類,在許多注解中我們都會(huì)看到它的身影,比如MyBatis在整合Spring時(shí),提供的@MapperScan注解:
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {
}
比如Spring開啟AOP功能的注解:
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
}
再比如Spring的異步調(diào)用注解:
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
}
大部分@Enable***注解的原理都差不多,都是通過利用@Import注解向Spring容器注入一些bean來實(shí)現(xiàn)的。
2. 用法
@Import注解可以引入三種類。
1、引入普通類,將作為bean注冊(cè)到Spring容器。
@Configuration
@Import(Person.class)
public class Application {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
Person person = context.getBean(Person.class);
}
}
2、引入ImportSelector實(shí)現(xiàn)類,Spring會(huì)將**selectImports()**方法返回的bean數(shù)組注冊(cè)到Spring容器,但是ImportSelector對(duì)象本身不會(huì)注冊(cè)到容器。
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.javap.importt.Person"};
}
}
3、引入ImportBeanDefinitionRegistrar實(shí)現(xiàn)類,Spring會(huì)暴露 BeanDefinitionRegistry對(duì)象,你可以自由的往容器里面注冊(cè)BeanDefinition,但是ImportBeanDefinitionRegistrar對(duì)象本身不會(huì)注冊(cè)到容器。
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 往registry注冊(cè)BeanDefinition
}
}
MyBatis整合Spring時(shí),提供的@MapperScan注解引入的就是ImportBeanDefinitionRegistrar的實(shí)現(xiàn)類,MyBatis會(huì)去掃描指定的包路徑,然后將Mapper接口對(duì)應(yīng)的BeanDefinition注冊(cè)到容器里。
3. 源碼分析
解析類上的@Import注解的職責(zé)Spring交給了ConfigurationClassPostProcessor類,它是BeanFactoryPostProcessor的子類,也就是說它屬于BeanFactory的擴(kuò)展點(diǎn)之一,它會(huì)處理容器內(nèi)所有的ConfigurationClass。
ConfigurationClassPostProcessor首先會(huì)過濾出容器內(nèi)所有的ConfigurationClass,然后實(shí)例化一個(gè)ConfigurationClassParser解析器去解析ConfigurationClass,解析過程中就會(huì)處理類上的@Import注解了,方法是ConfigurationClassParser#processImports()。
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, 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) {
if (candidate.isAssignable(ImportSelector.class)) {
// 引入的是ImportSelector子類
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
} else {
// 調(diào)用子類方法,得到要引入的beanNames
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
} else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// 引入的是ImportBeanDefinitionRegistrar子類
Class<?> candidateClass = candidate.loadClass();
// 實(shí)例化子類對(duì)象
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
// 暫存到Map,稍后觸發(fā)
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
} else {
// 引入的是普通類,正常注入到容器
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
// 引入的類可能是ConfigurationClass,遞歸處理
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
} 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();
}
}
}方法做了三件事:
- 如果引入的是ImportSelector子類,實(shí)例化子類對(duì)象,調(diào)用
selectImports()方法得到要注冊(cè)的beanNames,完成注冊(cè)。 - 如果引入的是ImportBeanDefinitionRegistrar子類,實(shí)例化子類對(duì)象,暫存到Map,稍后觸發(fā)。
- 如果引入的是普通類,正常注冊(cè)到容器。
對(duì)于ImportBeanDefinitionRegistrar子類,這里只是實(shí)例化了子類對(duì)象然后暫存到Map容器中,此時(shí)還沒有去觸發(fā)ImportBeanDefinitionRegistrar#registerBeanDefinitions()方法。方法的觸發(fā)是在ConfigurationClass解析完以后,ConfigurationClassPostProcessor會(huì)調(diào)用ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsFromRegistrars()方法遍歷Map容器中所有的ImportBeanDefinitionRegistrar子類對(duì)象,挨個(gè)觸發(fā)擴(kuò)展方法。
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
registrars.forEach((registrar, metadata) ->
registrar.registerBeanDefinitions(metadata, this.registry));
}
到此這篇關(guān)于Spring注解之@Import使用方法講解的文章就介紹到這了,更多相關(guān)Spring注解@Import內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決grails服務(wù)端口沖突的辦法(grails修改端口號(hào))
grails中默認(rèn)的服務(wù)端口為8080,當(dāng)本機(jī)中需要同時(shí)啟動(dòng)兩個(gè)不同的項(xiàng)目時(shí),就會(huì)造成端口沖突,下面給出解決方法2013-12-12
大數(shù)據(jù) java hive udf函數(shù)的示例代碼(手機(jī)號(hào)碼脫敏)
這篇文章主要介紹了大數(shù)據(jù) java hive udf函數(shù)(手機(jī)號(hào)碼脫敏),的相關(guān)知識(shí),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06
SpringBoot使用minio進(jìn)行文件管理的流程步驟
MinIO 是一個(gè)高性能的對(duì)象存儲(chǔ)系統(tǒng),兼容 Amazon S3 API,該軟件設(shè)計(jì)用于處理非結(jié)構(gòu)化數(shù)據(jù),如圖片、視頻、日志文件以及備份數(shù)據(jù)等,本文給大家介紹了SpringBoot使用minio進(jìn)行文件管理的流程步驟,需要的朋友可以參考下2025-01-01

