一. @Import
注解的功能
-
引入其他的
@Configuration
下例中, 在ConfigB
中聲明 @BeanServiceB
, 在ConfigA
中引入ConfigB
, 并用ConfigA
配置容器. 發(fā)現(xiàn)容器中包含ServiceB
bean. 證明 @Import 生效, 起到和 <import> 標簽相同的功能///////////// ConfigA @Import(ConfigB.class) @Configuration public class ConfigA {} ///////////// ConfigB @Configuration public class ConfigB { @Bean public ServiceInterface getServiceB() { return new ServiceB(); } } ///////////// Service public interface ServiceInterface { void test(); } class ServiceB implements ServiceInterface { @Override public void test() { System.out.println("ServiceB"); } } ///////////// Main 函數(shù) public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(ConfigA.class); ctx.refresh(); // out: ServiceB ctx.getBean(ServiceInterface.class).test(); } }
-
實例化其它 bean
當@Import
指定的類不是 @Configuration 配置類時, 容器會把類當成 bean 導入.
例如把上面代碼中的 ConfigA 的 @Import 修改為@Import(ServiceB.class)
,就會生成 ServiceB 的 Bean 到容器中@Import(ServiceB.class) @Configuration public class ConfigA {}
-
@Import(ImportSelector.class)
導入ImportSelector
中返回的 class name 數(shù)組, 可以不把要導入的 Configuration 類或 Bean 類寫在 @Import 中, 而是傳入一個繼承ImportSelector
的子類, 覆蓋String[] selectImports()
方法, 返回導入的類名數(shù)組public interface ImportSelector { String[] selectImports(AnnotationMetadata importingClassMetadata); // 返回 包名.類名 即可 }
一般的, 框架中如果要基于 AnnotationMetaData 做動態(tài)加載類, 一般會把
@Import(ImportSelector.class)
寫在自定義的注解上, 讓自定義注解提供額外的動態(tài)信息.//////////////// 自定義注解提供額外的 name 屬性 @Retention(RetentionPolicy.RUNTIME) @Documented @Target(ElementType.TYPE) @Import(ServiceImportSelector.class) @interface MyEnableService{ String name(); } //////////////// ImportSelector 實現(xiàn)動態(tài)導入 class ServiceImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata metaData) { MultiValueMap<String, Object> attributes = metaData.getAllAnnotationAttributes(MyEnableService.class.getName(), true); String nameAttr = attributes.get("name").get(0).toString(); if ("B".equals(nameAttr)){ return new String[]{"annotation.testImport.ServiceB"}; } else { return new String[]{"annotation.testImport.ServiceA"}; } } } //////////////// 被 @MyEnableService 注解的 Config 類 @MyEnableService(name = "B") @Configuration public class ConfigA {}
此外, @Import 也可以填入
DeferredImportSelector
接口的實現(xiàn)類, 同ImportSelector
接口效果一樣, 只是前者生成 bean 的時機在后 -
@import(ImportBeanDefinitionRegistrar.class) - 構造修改 BeanDefinition
使用ImportBeanDefinitionRegistrar
子類, 修改 bean 的BeanDefinition
. 比如, 前面的 ImportSelector , 實例化 bean 時會調用 bean 的無參構造函數(shù). 如果想調用帶參構造函數(shù)實例化 bean, 要在 BeanDefinition 中加入構造參數(shù)
如下例子, ServiceC 的構造函數(shù)有 name 屬性, 要正確實例化, 要在 Import 中構造 BeanDefinition//////////////// Bean 只有帶參構造函數(shù) class ServiceC implements ServiceInterface { private String name; public ServiceC(String name) { this.name = name; } @Override public void test() { System.out.println("ServiceC (name:)" + this.name); } } //////////////// 自定義注解提供額外的 name 屬性 @Retention(RetentionPolicy.RUNTIME) @Documented @Target(ElementType.TYPE) //@Import(ServiceImportSelector.class) @Import(ServiceBeanDefinitionRegistrar.class) @interface MyEnableService{ String name(); } //////////////// ImportBeanDefinitionRegistrar 實現(xiàn)動態(tài)導入 class ServiceBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata metaData, BeanDefinitionRegistry registry) { MultiValueMap<String, Object> attributes = metaData.getAllAnnotationAttributes(MyEnableService.class.getName(), true); String nameAttr = attributes.get("name").get(0).toString(); if ("C".equals(nameAttr)){ BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ServiceC.class). addConstructorArgValue("zhangsan"); registry.registerBeanDefinition("serviceC", builder.getBeanDefinition()); } else { BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ServiceB.class); registry.registerBeanDefinition("serviceB", builder.getBeanDefinition()); } } } //////////////// 被 @MyEnableService 注解的 Config 類 @MyEnableService(name = "C") @Configuration public class ConfigA {} //////////////// Main public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(ConfigA.class); ctx.refresh(); // out: ServiceC (name:)zhangsan ctx.getBean(ServiceInterface.class).test(); } }
二. @Import
注解解析時刻
加載解析 @Import
注解位于 BeanFactoryPostProcessor
被處理的時候:
AbstractApplicationContext
的 refresh()
方法
->
invokeBeanFactoryPostProcessors(beanFactory);
->
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
-
->
registryProcessor.postProcessBeanDefinitionRegistry(registry);
這里的registryProcessor,我們指 ConfigurationClassPostProcessor
->
ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(registry)
-
->
processConfigBeanDefinitions(registry)
// ConfigurationClassPostProcessor public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { //省略一些配置檢查與設置的邏輯 //根據 @Order 注解,排序所有的 @Configuration 類 configCandidates.sort((bd1, bd2) -> { int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); return Integer.compare(i1, i2);}); // 創(chuàng)建 ConfigurationClassParser 解析 @Configuration 類 ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); //剩余沒有解析的 @Configuration 類 Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); //已經解析的 @Configuration 類 Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); do { // 關鍵解析步驟 parser.parse(candidates); ... // this.reader.loadBeanDefinitions(configClasses); } while (!candidates.isEmpty()); //省略后續(xù)清理邏輯 }
-
上面的 parser.parse() 最終調用
doProcessConfigurationClass()
該方法同時處理@Component
,@ComponentScan
,@Import
等重要注解protected final SourceClass doProcessConfigurationClass( ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter) throws IOException { //處理`@Component`注解的MemberClass相關代碼... //處理`@PropertySource`注解相關代碼... //處理`@ComponentScan`注解相關代碼... //處理`@Import`注解: processImports(configClass, sourceClass, getImports(sourceClass), filter, true); //處理`@ImportResource`注解相關代碼... //處理`@Bean`注解相關代碼... //處理接口方法相關代碼... //處理父類相關代碼... }