Spring Framework自3.1开始,支持“@Enable 模块驱动”。其意义在于能够简化装配步骤,实现“按需装配”,同时屏蔽组件集合装配的细节。Spring Framework在实现上分为两类:“注解驱动 ”和“接口编程 ”。两种实现均需要依赖@Import
,其职责是导入一个或多个组件类( component classes ),将其定义为Spring Bean。 这里的组件类分两种:
被标注@Configuration
的类;普通的组件类,作用类似AnnotationConfigApplicationContext#register(对应注解驱动)
ImportSelector
或者ImportBeanDefinitionRegistrar
接口的实现类(对应接口编程)。
基于”注解驱动” @EnableWebMvc
注解是基于“注解驱动”的例子:
1 2 3 4 5 6 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(DelegatingWebMvcConfiguration.class) public @interface EnableWebMvc {}
1 2 3 @Configuration(proxyBeanMethods = false) public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {}
基于“接口编程” 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public interface ImportSelector { String[] selectImports(AnnotationMetadata importingClassMetadata); @Nullable default Predicate<String> getExclusionFilter () { return null ; } }
ImportSelector
接口的作用是判断哪些被@Configuration
标注的类应该被导入,判断条件一般是一个或多个注解属性。ImportSelector
接口定义了一个selectImports 方法,返回需要被导入的类的全称限定名。入参是正在被导入的@Configuration
类的AnnotationMetadata
。这里正在被导入的@Configuration
类是指@Import
标注的类,在“**@Enable 模块驱动”中指被 @Enable**标注的类。
1 2 3 4 5 6 7 8 9 10 public interface ImportBeanDefinitionRegistrar { default void registerBeanDefinitions (AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) { registerBeanDefinitions(importingClassMetadata, registry); } default void registerBeanDefinitions (AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { } }
ImportBeanDefinitionRegistrar
接口相对于ImportSelector
而言,多了一个BeanDefinitionRegistry
,作为入参,将Bean定义(BeanDefinition)的注册也交给了开发人员。
@Enable模块驱动原理 在Spring应用上下文启动过程中(AbstractApplicationContext#refresh()方法被调用时),Spring容器(BeanFactory)将ConfigurationClassPostProcessor 初始化为Spring Bean,作为优先级最高的BeanFactoryPostProcessor实现,随后其postProcessBeanFactory方法被调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory) { int factoryId = System.identityHashCode(beanFactory); if (this .factoriesPostProcessed.contains(factoryId)) { throw new IllegalStateException( "postProcessBeanFactory already called on this post-processor against " + beanFactory); } this .factoriesPostProcessed.add(factoryId); if (!this .registriesPostProcessed.contains(factoryId)) { processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory); } enhanceConfigurationClasses(beanFactory); beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory)); }
列一下processConfigBeanDefinitions主要的步骤:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public void processConfigBeanDefinitions (BeanDefinitionRegistry registry) { List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); String[] candidateNames = registry.getBeanDefinitionNames(); for (String beanName : candidateNames) { BeanDefinition beanDef = registry.getBeanDefinition(beanName); ... else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this .metadataReaderFactory)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } } ... ConfigurationClassParser parser = new ConfigurationClassParser( this .metadataReaderFactory, this .problemReporter, this .environment, this .resourceLoader, this .componentScanBeanNameGenerator, registry); ... Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); ... parser.parse(candidates); ... }
parse方法将@Configuration类包装为ConfigurationClass
,然后调用processConfigurationClass方法。
1 2 3 4 5 6 7 8 9 10 11 protected void processConfigurationClass (ConfigurationClass configClass, Predicate<String> filter) throws IOException { ... SourceClass sourceClass = asSourceClass(configClass, filter); do { sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter); } while (sourceClass != null ); this .configurationClasses.put(configClass, configClass); }
processConfigurationClass方法又调用了doProcessConfigurationClass进行处理。doProcessConfigurationClass方法会处理很多注解,例如@Component
、@PropertySource
、@ComponentScan
、@Bean
方法等。我们这里只关注对@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 processImports(configClass, sourceClass, getImports(sourceClass), filter, true ); private void processImports (ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter, boolean checkForCircularImports) { ... 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); } } }
可以看到processImports 方法会处理ImportSelector
实现、ImportBeanDefinitionRegistrar
实现,其他情况会视为@Configuration
类,再调用回processConfigurationClass 方法。最后ConfigurationClass
集合将被注册为Spring Bean。
ConfigurationClassPostProcessor 不仅检查@Configuration
和@Bean
两种Bean定义方式,还处理@Component
。@Configuration
类标注为“完全模式”,而@Component
和@Bean
方法则是“轻量模式”。最后使用CGLib实现ConfigurationClassEnhancer对@Configuration
类进行提升。
综上所述,ConfigurationClassPostProcessor 负责筛选@Component
类、@Configuration
类及@Bean
方法的BeanDefinition,ConfigurationClassParser 则从候选的Bean定义中解析出ConfigurationClass
集合并注册成Spring Bean。