前言
分析SpringBoot启动流程时遇到的@Import
注解,不明所以然,所以先翻一下注解的源码。
源码
1 2 3 4 5 6 7 8 9 10 11
| @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Import {
Class<?>[] value(); }
|
使用方式
简单使用方法
这种方式可以直接把类加入到Spring IOC容器
1 2 3
| @Configuration @Import(value={UserServiceImpl.class}) public class Config {}
|
这种方式很少用,原因有二:
- 直接使用
@Bean
注解代替更加方便
- 仅能调用无参构造
结合ImportBeanDefinitionRegistrar
接口
源码
1 2 3 4 5 6 7 8 9 10 11
| public interface ImportBeanDefinitionRegistrar { default void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, // 通过这个参数可以拿到类的元数据信息 BeanDefinitionRegistry registry, // 通过这个参数可以操作IOC容器 BeanNameGenerator importBeanNameGenerator) { registerBeanDefinitions(importingClassMetadata, registry); } default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {} }
|
实现类
1 2 3 4 5 6 7
| public class UserServiceBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) { BeanDefinitionBuilder userService = BeanDefinitionBuilder.rootBeanDefinition(UserServiceImpl.class); registry.registerBeanDefinition("userService", userService.getBeanDefinition()); } }
|
使用方式
1 2 3
| @Configuration @Import(value={UserServiceBeanDefinitionRegistrar.class}) public class Config {}
|
结合ImportSelector
接口
相比较与实现ImportBeanDefinitionRegistrar
接口之后直接操作Bean容器来说,使用ImportSelector
会更加优雅一些,只需要返回需要注入类的全名即可,也可以同时注入多个Bean,Spring 中EnableAutoConfiguration
注解就是用的这种方式。
源码
1 2 3 4 5 6 7 8 9
| public interface ImportSelector { String[] selectImports(AnnotationMetadata importingClassMetadata); @Nullable default Predicate<String> getExclusionFilter() { return null; } }
|
实现类
1 2 3 4 5
| public class UserServiceImportSelect implements ImportSelector{ public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{UserServiceImpl.class.getName()}; } }
|
使用方式
1 2 3
| @Configuration() @Import(value={UserServiceImportSelect.class}) public class Config {}
|
Spring 处理@Import的源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter, boolean checkForCircularImports) { if (importCandidates.isEmpty()) { return; } if (checkForCircularImports && isChainedImportOnStack(configClass)) { this.problemReporter.error(new CircularImportProblem(configClass, this.importStack)); } else { this.importStack.push(configClass); try { for (SourceClass candidate : importCandidates) { if (candidate.isAssignable(ImportSelector.class)) { Class<?> candidateClass = candidate.loadClass(); ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class, this.environment, this.resourceLoader, this.registry); Predicate<String> selectorFilter = selector.getExclusionFilter(); if (selectorFilter != null) { exclusionFilter = exclusionFilter.or(selectorFilter); } if (selector instanceof DeferredImportSelector) { this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); } else { String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter); processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false); } } else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) { Class<?> candidateClass = candidate.loadClass(); ImportBeanDefinitionRegistrar registrar = ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class, this.environment, this.resourceLoader, this.registry); configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); } else { this.importStack.registerImport( currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter); } } } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]", ex); } finally { this.importStack.pop(); } } }
|
总结
最方便的是结合ImportSelector
方式,主要作用是使用注解注入Bean
到IOC
容器