spring @Import注解 分析

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容器中

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值