1.@Import注解的作用
@Import只能用在类上 ,@Import通过快速导入的方式实现把实例加入spring的IOC容器中。
加入IOC容器的方式有很多种,@Import注解就相对更便捷,@Import注解可以用于导入第三方包 ,当然@Bean注解也可以,但是@Import注解快速导入的方式更加便捷。
2.@Import注解的三种用法
@Import的三种用法主要包括:
1、直接填class数组方式
2、ImportSelector方式【重点】
3、ImportBeanDefinitionRegistrar方式
第一种用法 直接填class数组 :
直接填对应的class数组,class数组可以有0到多个。
语法如下:
@Import({ 类名.class , 类名.class... })
public class TestDemo {
}
对应的import的bean都将加入到spring容器中,这些在容器中bean名称是该类的全类名 ,比如com.chr.test.类名
第二种用法:ImportSelector方式【重点】
这种方式的前提就是一个类要实现ImportSelector接口,假如我要用这种方法,目标对象是MyImportSelector这个类,分析具体如下:
创建MyImportSelector类并实现ImportSelector接口
/**
* @author chenhairong3
* @description 使用ImportSelector的方式把对象引入spring ioc容器
* @date 2020/12/21
*/
public class MyImportSelector implements ImportSelector {
//实现接口中的方法
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{"com.chr.test.importsample.ImportedDemo1"};
}
}
分析实现接口的selectImports方法中的:
- 1、返回值: 就是我们实际上要导入到容器中的组件全类名【重点 】
- 2、参数: AnnotationMetadata表示当前被@Import注解给标注的所有注解信息【不是重点】
需要注意的是selectImports方法可以返回空数组但是不能返回null,否则会报空指针异常!
/**
* @author chenhairong3
* @description
* @date 2020/12/21
*/
public class ImportedDemo1 {
}
/**
* @author chenhairong3
* @description
* @date 2020/12/21
*/
@Import({Demo3.class,MyImportSelector.class})
public class TestDemo {
@Bean
public UserDao userDao(){
return new UserDao();
}
}
public class UserDao {
public UserDao(){
System.out.println("UserDao 无参构造函数被调用。");
}
}
/**
* @author chenhairong3
* @description
* @date 2020/12/21
*/
public class Demo3 {
}
编写测试类:
/**
* @author chenhairong3
* @description
* @date 2020/12/21
*/
public class TestImport {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestDemo.class);
String[] beanDefinitons = context.getBeanDefinitionNames();
for (String beanName : beanDefinitons) {
System.out.println("beanDefinition = " + beanName);
}
}
}
输出:
标红的部分是spring自身的bean。标绿的是我们自定义的bean.可以看到我们自定义的bean通过@Import注解和ImportSelector的方式已经被引入到spring ioc容器中了。
第三种用法:ImportBeanDefinitionRegistrar方式
同样是一个接口,类似于第二种ImportSelector用法,只不过这种用法比较自定义化注册,具体如下:
第一步:创建Myclass2类并实现ImportBeanDefinitionRegistrar接口
/**
* @author chenhairong3
* @description 通过实现ImportBeanDefinitionRegistrar接口,实现自定义注册bean到ioc容器中
* @date 2020/12/21
*/
public class MyClass2 implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata,
BeanDefinitionRegistry beanDefinitionRegistry) {
RootBeanDefinition demo3Definiton = new RootBeanDefinition();
//可以自定义一些BeanDefinition的属性,
demo3Definiton.setScope("singleton");
beanDefinitionRegistry.registerBeanDefinition("demo3", demo3Definiton);
}
}
写一个类import 引入新写的MyClass2
/**
* @author chenhairong3
* @description
* @date 2020/12/21
*/
@Import({MyClass2.class})
public class TestDemo2 {
}
写测试类:
/**
* @author chenhairong3
* @description
* @date 2020/12/21
*/
public class TestImportBeanDefinitionRegistrar {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestDemo2.class);
String[] beanDefinitons = context.getBeanDefinitionNames();
for (String beanName : beanDefinitons) {
System.out.println("beanDefinition = " + beanName);
}
}
}
输出:
3.原理分析
在一篇文章分析@Configuration注解的时候,
https://blog.csdn.net/hairongok353043059/article/details/111329646
我们分析源码:
在ConfigurationClassParser类中:
在parse方法中:
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
this.processConfigurationClass(new ConfigurationClass(metadata, beanName));
}
进入doProcessConfigurationClass方法:
@Nullable
protected final ConfigurationClassParser.SourceClass doProcessConfigurationClass(ConfigurationClass configClass, ConfigurationClassParser.SourceClass sourceClass) throws IOException {
this.processMemberClasses(configClass, sourceClass);
Iterator var3 = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class, PropertySource.class).iterator();
AnnotationAttributes importResource;
while(var3.hasNext()) {
importResource = (AnnotationAttributes)var3.next();
if (this.environment instanceof ConfigurableEnvironment) {
this.processPropertySource(importResource);
} else {
this.logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment");
}
}
//处理ComponetScan的地方
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
Iterator var13 = componentScans.iterator();
while(var13.hasNext()) {
AnnotationAttributes componentScan = (AnnotationAttributes)var13.next();
Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
Iterator var7 = scannedBeanDefinitions.iterator();
while(var7.hasNext()) {
BeanDefinitionHolder holder = (BeanDefinitionHolder)var7.next();
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
this.parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
//处理import注解的地方
this.processImports(configClass, sourceClass, this.getImports(sourceClass), true);
//处理importResource的地方
importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
String[] var19 = resources;
int var21 = resources.length;
for(int var22 = 0; var22 < var21; ++var22) {
String resource = var19[var22];
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
//处理bean注解的地方
Set<MethodMetadata> beanMethods = this.retrieveBeanMethodMetadata(sourceClass);
Iterator var17 = beanMethods.iterator();
while(var17.hasNext()) {
MethodMetadata methodMetadata = (MethodMetadata)var17.next();
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
this.processInterfaces(configClass, sourceClass);
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
return sourceClass.getSuperClass();
}
}
return null;
}
进入 //处理import注解的地方
this.processImports(configClass, sourceClass, this.getImports(sourceClass), true);
this.getImports(sourceClass),会找到所有有import注解的类:
private Set<ConfigurationClassParser.SourceClass> getImports(ConfigurationClassParser.SourceClass sourceClass) throws IOException {
Set<ConfigurationClassParser.SourceClass> imports = new LinkedHashSet();
Set<ConfigurationClassParser.SourceClass> visited = new LinkedHashSet();
this.collectImports(sourceClass, imports, visited);
return imports;
}
这里会把Import注解的上对应的value中引入的类放入imports集合中。
然后进入processImport方法:
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) throws IOException {
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {// ====1
// Import注解中配置的是ImportSelector类型
Class<?> candidateClass = candidate.loadClass();
//实例化
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
// 如果该类有实现对应的Aware接口,则注入对应的属性
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
// DeferredImportSelector类型的放到集合中待后续处理
if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {//====1.1
this.deferredImportSelectors.add(
new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
}
else {//====1.2
// 非DeferredImportSelector类型,例如我们的写的Demo
// 直接调用selectImports方法取得对应的类
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
// 在调用processImports进行处理
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {//====2
// 如果是ImportBeanDefinitionRegistrar类型
Class<?> candidateClass = candidate.loadClass();
// 实例化
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
// 如果该类有实现对应的Aware接口,则注入对应的属性
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
// 同1.1先放到集合中待后续处理
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {//====3
// 如果非上面两种类型,那么代表这是一个与@Configuration相关的类
// 交由processConfigurationClass来进行Configuration进行
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
总结:
不会放到spring容器中的情况:
1.使用@Import标签引入的ImportSelector或ImportBeanDefinitionRegistrar的实现类本身不会放到spring容器中
2.使用@Import标签引入的ImportSelector实现类,对应的实现方法selectImports返回的类,如果还是ImportSelector或者ImportBeanDefinitionRegistrar实现类,也不会放到容器中
放到spring容器中的情况:
1.直接通过@Import引入没有实现ImportSelector和ImportBeanDefinitionRegistrar接口的普通类
2.使用@Import引入一个ImportSelector实现类,selectImports方法返回没有实现ImportSelector和ImportBeanDefinitionRegistrar接口的普通类全限定名字符串
3.使用@Import引入ImportBeanDefinitionRegistrar的实现类,直接用方法registerBeanDefinitions中参数registry注册器,注册bean到spring容器中