@Import 注解 用法解析

187 阅读3分钟

@Import 的作用?

1、初始化 Bean,将其添加至容器中

2、加载其他的@Configuration

3、指定实现 ImportSelector (以及 DefferredServiceImportSelector )的类,用于个性化加载

4、指定实现 ImportBeanDefinitionRegistrar 的类,用于个性化加载

注册 Bean 的几种方式

@Configuration 配置类中注册

1、包扫描:@ComponentScan + 组件注解:@Service@ControllerComponent@Repository

2、@Bean

3、@Import

4、FactoryBean 的 getObject()

这里介绍 @Import 方式

为什么要用 @Import ?

一般地,我们写代码时,想把谁扔到容器里交给 Spring 容器管理,直接在类上添加一个组件注解就完事了(如 @Controller ),那为什么要使用 @Import 呢?

直接@Component 他不香了吗?如果使用这个 @Import,反倒要多敲几个字母的代码?

@Import(User.class) // @Import 导入的默认是全限定类名
public class ConfigurationA {
}    

原因是:

导入的组件不只有我们自己写的,有时候要添加第三方包中的类,但第三方包中的类却没标注有 “组件” 注解。这时候就要通过 @Import 注解来完成快速导入

当然地,使用 @Bean也能达到同样的效果,但要多写一个方法,而且这里先谈 @Import

用法一

直接根据类导入:XXX.Class

id 默认是全限定类名

我们看到,@Import 注解里的属性是一个类的集合,注解初始定义源码如下

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
    Class<?>[] value();
}

所以说只需要在注解中添加上所需要添加至 Spring 容器的类即可

比如:

@Import(User.class)
public class ConfigurationA { }

这样子就能在容器中获取到这个 User 类的实例对象了

@Test
void testConfiguration() {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ConfigurationB.class);
    
    // 注意!这里一定要写全限定类名!
    System.out.println(ac.getBean("com.gg.springboot.entity.User")); 
}

注意的问题:

在使用 getBean() 时,若要获取 @Import 注解导入的类,则需要写全限定类名!

用法二

使用 ImportSelector

源码注释有说明

image.png

1、创建 ImportSelector 的实现类并重写selectImports()方法

public class MyImportSelector implements ImportSelector {
    /**
     * @param importingClassMetadata : 当前标注了 @Import 注解的类的所有类信息,以及类上的其他注解信息
     * @return 返回值,就是要导入容器中的 bean 的全限定类名
     */
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        // 这里可以写 @Configuration 修饰的类的全限定类名,也可以写普通实体类的全限定类名
        return new String[]{"com.gg.springboot.entity.Car"};
    }
}

2、@Import 注解引入此实现类

@Configuration
@Import({User.class, MyImportSelector.class}) // @Import 导入的默认是全限定类名
public class ConfigurationA { }

完工

应用场景

有没有很眼熟?没错,在 SpringBoot 的自动配置原理时有提到这个导入方式:SpringBoot自动配置原理解析

@EnableAutoConfiguration 里面 Import 了一个 AutoConfigurationImportSelector 的类

捋一捋这个类是怎么一回事,这个类的实现关系是这样子的

AutoConfigurationImportSelector ---实现---> DeferredImportSelector ---继承---> ImportSelector

image.png 然后再看看 AutoConfigurationImportSelector 方法的源码

// 有没有发现就是这个方法???
// 方法重写
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
   if (!isEnabled(annotationMetadata)) {
      return NO_IMPORTS;
   }
   AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
   return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
​
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    // xxxxxxxxxxxxxxxx
}

用法三

使用 ImportBeanDefinitionRegistrar

源码注释有说明

image.png

1、创建 ImportBeanDefinitionRegistrar 的实现类并重写registerBeanDefinitions()方法

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // RootBeanDefinition 作为参数的作用:
        // 1、指定类名。2、指定类的定义信息(Bean 的类型、scope 作用域等信息......)
        registry.registerBeanDefinition("myCar",new RootBeanDefinition(Car.class));
    }
}

2、@Import 注解引入此实现类

@Configuration
@Import({MyImportBeanDefinitionRegistrar.class}) // @Import 导入的默认是全限定类名
public class ConfigurationA {
    
}

应用场景(挖个坑)

SpringBoot 源码中常用