Spring系列之@Import

@Import简介

@Import表示用来导入配置类或者一些需要前置加载的类.。
@Import支持 三种方式
1.带有@Configuration的配置类(4.2 版本之前只可以导入配置类, 4.2版本之后 也可以导入 普通类)
2.ImportSelector 的实现
3.ImportBeanDefinitionRegistrar 的实现

导入@Configuration的配置类

代码结构

1、代码结构见上图,RootConfig和OtherConfig在两个目录中

//ComponentScan包扫描,默认扫描当前路径下所有文件和子目录文件
//所有会扫描到RootConfig而扫描不到OtherConfig
@ComponentScan
public class SpringApplicationContext {
    public static void main(String[] args) {
    	//创建Annotation上下文
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringApplicationContext.class);
        //得到AService的bean
        AService bean = context.getBean(AService.class);
        bean.service();
        //得到BService的bean
        BService bean2 = context.getBean(BService.class);
        bean2.service();
        //关闭容器
        context.close();
    }
}

2、先看RootConfig 和AService代码

//Configuration等同于xml配置
//类中@bean注解的会被注册到spring容器中
//@import 将另外一个Configuration导入到当前Configuration中
//等于xml中<import>另一个xml
@Configuration
@Import(OtherConfig.class)
public class RootConfig {
    @Bean
    public AService orderDemoService(){
        return new AService();
    }
}

//AService 无任何注解,表示没有交给Spring管理
public class AService {
    public void service(){
        System.out.println("AService run");
    }
}

3、再看OtherConfig 和BService代码

//Configuration等同于xml配置
//类中@bean注解的会被注册到spring容器中
@Configuration
public class OtherConfig {
    @Bean
    public BService omsService(){
        return new BService();
    }
}
//BService 无任何注解,表示没有交给Spring管理
public class BService {
    public void service(){
        System.out.println("BService run");
    }
}

4、最后看执行效果
执行结果
执行结果表示,AService和BService 都是spring的bean,交给容器管理,说明此处import见效。

总结下执行过程:

  1. 容器类注解@ComponentScan,故启动时会扫描当前路径
  2. 扫描到RootConfig类,由于RootConfig类注解@Import(OtherConfig.class),故扫描OtherConfig类
  3. RootConfig和OtherConfig中分别存在@Bean注解,会将@Bean注解的方法的返回值放入Spring容器中
  4. 容器启动后AService和BService都在容器中,故可getBean拿到bean后使用

导入ImportSelector 的实现

1、先看启动类

//导入ImportSelector的实现类
//面向接口编程
@ComponentScan
@Import(MySelector.class)
public class SpringApplicationContext {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringApplicationContext.class);
        //得到MyBean的bean
        MyBean myBean = context.getBean(MyBean.class);
        myBean.service();
        //关闭容器
        context.close();
    }
}

2、继续上MySelector 和MyBean代码

//实现ImportSelector接口中selectImports方法
public class MySelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{MyBean.class.getName()};
    }
}
//MyBean 无任何注解,表示没有交给Spring管理
public class MyBean {
    public void service(){
        System.out.println("MyBean run");
    }
}

3、最后看运行结果
运行结果
执行结果表示,MyBean在容器中,说明此处import见效。

总结下执行过程:

  1. 容器类注解@ComponentScan,故启动时会扫描当前路径
  2. 扫描启动类时发现@Import(MySelector.class),会取出MySelector类中selectImports方法的返回值,返回值为String数组,数组内容为类的全限定名
  3. 容器会加载这些类,我们示例中会加载MySelector
  4. 容器启动后MySelector在容器中,故可getBean拿到bean后使用

ImportBeanDefinitionRegistrar 的实现

1、先看启动类

//导入ImportBeanDefinitionRegistrar的实现类
//可批量加入bean
@ComponentScan
@Import(MyImportBeanDefinitionRegistrar.class)
public class SpringApplicationContext {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringApplicationContext.class);
        //得到类的实例
        MyRegistrarBean myRegistrarBean = context.getBean(MyRegistrarBean.class);
        myRegistrarBean.service();
        //关闭容器
        context.close();
    }
}

2、继续上MyImportBeanDefinitionRegistrar和MyRegistrarBean代码

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        String beanName = MyRegistrarBean.class.getSimpleName();
        //beanName转化为驼峰式
        beanName = StringUtils.uncapitalize(beanName);
        //新建RootBeanDefinition放入BeanDefinition注册器中
        RootBeanDefinition beanDefinition = new RootBeanDefinition(MyRegistrarBean.class);
        registry.registerBeanDefinition(beanName,beanDefinition);
    }
}
public class MyRegistrarBean {
    public void service(){
        System.out.println("MyRegistrarBean service");
    }
}

3、最后看运行结果
运行结果执行结果表示,MyRegistrarBean 在容器中,说明此处import见效。

总结下执行过程:

  1. 容器类注解@ComponentScan,故启动时会扫描当前路径
  2. 扫描启动类时发现@Import(MyImportBeanDefinitionRegistrar.class),因为我们在MyImportBeanDefinitionRegistrar类中往BeanDefinition注册器中添加了MyRegistrarBean的BeanDefinition
  3. 容器会加载这些类
  4. 容器启动后MyRegistrarBean 在容器中,故可getBean拿到bean后使用
  5. ImportBeanDefinitionRegistrar因为直接添加BeanDefinition,故更强大灵活,也对开发者有一定的spring基础要求

总结

1、我们在springboot中经常见到导入ImportSelector和ImportBeanDefinitionRegistrar的代码,用的就是这个原理。
2、在日常开发中,我们可以通到配置XML或者外部资源配置管理类全限定名,起到手动加载自己想要的类的目的。
3、ImportSelector源码实现是在初始化beanFactory时完成解析,源码较复杂,有兴趣可自己查看。

 private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, boolean checkForCircularImports) throws IOException {
	  //删除无关代码
	  // Import 注解中配置的是 ImportSelector 类型
      if (candidate.isAssignable(ImportSelector.class)) { 
            Class<?> candidateClass = candidate.loadClass();
            ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class); // 实例化
            ParserStrategyUtils.invokeAwareMethods(selector, this.environment, this.resourceLoader, this.registry); // 如果该类有实现对应的 Aware 接口,则注入对应的属性
            if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) { // DeferredImportSelector 类型的放到集合中待后续处理
                this.deferredImportSelectors.add(new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
            }
            else { // 直接调用 selectImports 方法取得对应的类
                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();
            ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
            ParserStrategyUtils.invokeAwareMethods(registrar, this.environment, this.resourceLoader, this.registry);
            configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
        }
        else {
            // 如果非上面两种类型,那么代表这是一个与 @Configuration 相关的类
            this.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
            processConfigurationClass(candidate.asConfigClass(configClass));
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值