Spring的@Import注解

作用

  1. 将没有使用@Component注解的普通class加入到Spring容器, 由Spring管理
  2. 导入一个Configuration类(比如你想组合多个Java Config类到一个Java Config类, 或者你引入的第三方jar包中的Java Config类没在你SpringBoot程序的子包下, 即没有被扫描进Spring容器)
  3. 通过实现了ImportSelector接口的类, 导入多个class到Spring容器(SpringBoot的自动装配@EnableAutoConfiguration)
  4. 通过实现ImportBeanDefinitionRegistrar接口的方式(MyBatis整合Spring: MapperScannerRegistrar.java和@MapperScan注解)

示例

1. 将普通Class加入到Spring容器

普通class

没有使用@Component注解, 有一个实例方法.

/**
 * 一个普通的class, 没有被打上{@link org.springframework.stereotype.Component}注解, 后续使用@Import导入到Spring容器
 *
 * @author rhyme
 */
@Slf4j
public class ClassWithoutComponentAnnotation {
  public boolean methodA() {
    log.info("ClassWithoutComponentAnnotation对象的**实例方法**methodA被调用");
    return true;
  }
}

2. @Import Java Config

将该类也和上面一样, 由@Import引入就行

@Configuration
public class ClassWithConfigurationAnnotation {
  @Bean
  public DemoClass demoClass() {
    return new DemoClass();
  }
}

3. 实现了ImportSelector接口的类, 导入多个class到Spring容器

实现了ImportSelector接口的类

/** @author rhyme */
public class ClassImplementsImportSelector implements ImportSelector {

  @Override
  public String[] selectImports(AnnotationMetadata importingClassMetadata) {
    return new String[] {
      "java.basic.demos.springannotation.Import.classimplementsimportselector.ClassA",
      "java.basic.demos.springannotation.Import.classimplementsimportselector.ClassB"
    };
  }
}

各种@EnableXXX注解, 比如SpringBoot源码中的@EnableAutoConfiguration@EnableCircuitBreaker

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

 String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

 /**
  * Exclude specific auto-configuration classes such that they will never be applied.
  * @return the classes to exclude
  */
 Class<?>[] exclude() default {};

 /**
  * Exclude specific auto-configuration class names such that they will never be
  * applied.
  * @return the class names to exclude
  * @since 1.3.0
  */
 String[] excludeName() default {};

}
/**
 * Annotation to enable a CircuitBreaker implementation.
 * https://martinfowler.com/bliki/CircuitBreaker.html
 * @author Spencer Gibb
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(EnableCircuitBreakerImportSelector.class)
public @interface EnableCircuitBreaker {

}

4. 实现ImportBeanDefinitionRegistrar接口的方式

实现了ImportBeanDefinitionRegistrar接口, 注册自己的bean

/** @author rhyme */
public class ClassImplementImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

  @Override
  public void registerBeanDefinitions(
      AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    RootBeanDefinition root = new RootBeanDefinition(ClassC.class);
    registry.registerBeanDefinition("classC", root);
  }
}

MyBatis整合Spring源码

/**
 * A {@link ImportBeanDefinitionRegistrar} to allow annotation configuration of
 * MyBatis mapper scanning. Using an @Enable annotation allows beans to be
 * registered via @Component configuration, whereas implementing
 * {@code BeanDefinitionRegistryPostProcessor} will work for XML configuration.
 *
 * @author Michael Lanyon
 * @author Eduardo Macarron
 * 
 * @see MapperFactoryBean
 * @see ClassPathMapperScanner
 * @since 1.2.0
 */
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {

5. 使用@Import搭配@Configuration导入到Spring容器

/**
 * @Import注解, 搭配@Configuration, 见ImportConfigTest.java<br>
 * 1. 将没有使用@Component注解的普通class加入到Spring容器, 由Spring管理<br>
 * 2. 导入一个Configuration类 <br>
 * 3. 通过实现了ImportSelector接口的类, 导入多个class到Spring容器(**SpringBoot的自动装配@EnableAutoConfiguration**) <br>
 * 4. 通过实现ImportBeanDefinitionRegistrar接口的方式(MyBatis整合Spring:MapperScannerRegistrar.java和@MapperScan注解)
 *
 * @author rhyme
 */
@Configuration
@Import({
  ClassWithoutComponentAnnotation.class,
  ClassWithConfigurationAnnotation.class,
  ClassImplementsImportSelector.class,
  ClassImplementImportBeanDefinitionRegistrar.class
})
public class ImportConfig {}

6. 测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class ImportConfigTest {
  @Autowired private ClassWithoutComponentAnnotation classWithoutComponentAnnotation;
  @Autowired private DemoClass demoClass;
  @Autowired private ClassA classA;
  @Autowired private ClassB classB;
  @Autowired private ClassC classC;

  /** 通过@Autowired获取@Import的类 */
  @Test
  public void importClass2SpringContainerTest() {
    Assert.assertNotNull(classWithoutComponentAnnotation);
    Assert.assertNotNull(demoClass);
    Assert.assertNotNull(classA);
    Assert.assertNotNull(classB);
    Assert.assertNotNull(classC);
  }
}