首先看下@Import的javadoc文档:
Provides functionality equivalent to the <import/> element in Spring XML. Allows for importing @Configuration classes, ImportSelector and ImportBeanDefinitionRegistrar implementations, as well as regular component classes (as of 4.2; analogous to AnnotationConfigApplicationContext.register).
@Bean definitions declared in imported @Configuration classes should be accessed by using @Autowired injection. Either the bean itself can be autowired, or the configuration class instance declaring the bean can be autowired. The latter approach allows for explicit, IDE-friendly navigation between @Configuration class methods.
May be declared at the class level or as a meta-annotation.
简单翻译一下,这里面说了3个点:
(1)@Import就是用来向容器中导入bean的,导入的方式有三种:导入@Configuration、导入ImportSelector、导入ImportBeanDefinitionRegistrar。
(2)被@Import的类是被加载到了Spring容器当中,因此无论是类本身还是类里面用@Bean注解定义的bean都可以被@Autowired注入。
(3)@Import可以加在类上面,也可以作为元注解加在注解上面
下面就来分别举例说明下。
1.导入@Configuration举例
//这是个普通的类
public class Service1 {public Service1(){System.out.println("Service1");}
}
//这是个配置类
@Configuration
public class DemoConfig {@Beanpublic Service2 service2(){return new Service2();}
}
@SpringBootApplication
//这样可以导入Service1和Service2这两个Bean
@Import({Service1.class, DemoConfig.class})
public class ImportDemoApplication {public static void main(String[] args) {SpringApplication.run(ImportDemoApplication.class, args);}
}
2.导入ImportSelector举例
public class DemoImportSelector implements ImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {//这里返回bean的完整的名字return new String[]{"com.github.xjs.importdemo.service.Service3"};}
}
@SpringBootApplication
//这样就可以导入Service3这个bean
@Import({DemoImportSelector.class})
public class ImportDemoApplication {public static void main(String[] args) {SpringApplication.run(ImportDemoApplication.class, args);}
}
3.导入ImportBeanDefinitionRegistrar举例
public class DemoRegistrar implements ImportBeanDefinitionRegistrar {@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {//可以直接向容器注册beanGenericBeanDefinition bd = new GenericBeanDefinition();bd.setBeanClass(Service4.class);registry.registerBeanDefinition("service4", bd);}
}
@SpringBootApplication
//这样就可以导入Service4这个bean
@Import({DemoRegistrar.class})
public class ImportDemoApplication {public static void main(String[] args) {SpringApplication.run(ImportDemoApplication.class, args);}
}
4.@Import作为元注解使用举例
因为@Import可以作为元注解使用,因此可以用这个特性来做组件扫描,比如:
//首先定义一个用于扫描的注解,上面添加@Import元注解:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Import({DemoImportSelector.class})
public @interface DemoScan {/*** 要扫描的根路径* */String basePackage() default "";
}
然后在启动类或者配置类上添加这个注解:
@SpringBootApplication
//首先添加这个注解
@DemoScan(basePackage="com.github.xjs")
@Import({DemoImportSelector.class, DemoRegistrar.class})
public class ImportDemoApplication {public static void main(String[] args) {SpringApplication.run(ImportDemoApplication.class, args);}
}
无论是ImportSelector还是ImportBeanDefinitionRegistrar在回调中都可以使用AnnotationMetadata获取@DemoScan里面的属性:
public class DemoImportSelector implements ImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {//读取DemoScan的属性AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(DemoScan.class.getName()));String basePackage = annoAttrs.getString("basePackage");System.out.println("basePackage:"+basePackage);return new String[]{"com.github.xjs.importdemo.service.Service3"};}
}
拿到了要扫描的路径如何去扫描可以参考上一篇如何去扫描Mapper接口的,在Spring的源码中有大量的这种用法,比如:ServletComponentScan与ServletComponentScanRegistrar、MapperScan与MapperScannerRegistrar等等。
5.@Import解析的源码
// org.springframework.context.annotation.ConfigurationClassParser:
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,Collection<SourceClass> importCandidates, boolean checkForCircularImports) {...for (SourceClass candidate : importCandidates) {if (candidate.isAssignable(ImportSelector.class)) {// Candidate class is an ImportSelector -> delegate to it to determine importsClass<?> candidateClass = candidate.loadClass();ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);ParserStrategyUtils.invokeAwareMethods(selector, this.environment, this.resourceLoader, this.registry);if (selector instanceof DeferredImportSelector) {this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);}else {String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);processImports(configClass, currentSourceClass, importSourceClasses, false);}}else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {// Candidate class is an ImportBeanDefinitionRegistrar ->// delegate to it to register additional bean definitionsClass<?> 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 {// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->// process it as an @Configuration classthis.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());processConfigurationClass(candidate.asConfigClass(configClass));}}...
}
}
完整的源码下载:
https://github.com/xjs1919/enumdemo 下的 import-demo
欢迎扫码加关注: