今天来说说@Enable模块驱动。Spring Framework是从 3.1
版本开始支持“@Enable模块驱动”的。所谓“模块”是指具备相同领域的功能组件集合,组合形成一个独立的单元。
图表是 Spring Framework
、 Spring Boot
和 spring Cloud
的@Enable注解模块:
框架实现 | @Enable注解模块 | 激活模块 |
---|---|---|
Spring Framework | @EnableWebMvc | Web Mvc模块 |
@EnableTransactionManagement | 事务管理模块 | |
@EnableCaching | Caching模块 | |
@EnableMBeanExport | JMX模块 | |
@EnableAsync | 异步处理模块 | |
@EnableWebFlux | Web Flux 模块 | |
@EnableAspectJAutoProxy | AspectJ模块 | |
Spring Boot | @EnableAutoConfiguration | 自动装配模块 |
@EnableManagementContext | Actuator模块 | |
@EnableConfigurationProperties | 配置属性绑定模块 | |
@EnableOAuth2Sso | OAuth2 单点登陆模块 | |
spring Cloud | @EnableEurekaServer | Eureka服务模块 |
@EnableConfigServer | 配置服务器模块 | |
@EnableFeignCliens | Feign客户端模块 | |
@EnableZuulProxy | 服务网关Zuul模块 | |
@EnableCircuitBreaker | 服务熔断模块 |
简化装配步骤
,实现了 “按需装配”
,同时屏蔽组件集合装配的细节。 这就要从Spring Framework3.0新引入的@Import说起。@Import用于导入一个或多个ConfigurationClass,将其注册为Spring Bean。这里需要注意的是:
Spring Framework3.0中存在一定的限制,仅支持@Configuration标注的类 在Spring Framework3.1中,@Import则扩大了指责范围,还可以用于声明至少一个@Bean方法的类,以及ImportSelector或ImportBeanDefinitionRegistrar的实现类
估计有小伙伴已经发现了,Spring Framework提供了 两类 关于@Enable模块驱动的实现方式:
“注解驱动” “接口编程”
1) 首先,我们举个栗子,拿@EnableAsync注解来说
:
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Import({AsyncConfigurationSelector.class}) public @interface EnableAsync { ... } 复制代码
我们发现上面@EnableAsync的源码标注了 @Import({AsyncConfigurationSelector.class})
。@Import的作用我已经提及过了,接下来点开AsyncConfigurationSelector类,看看是啥子东西:
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> { ... } 复制代码
好,我们发现AsyncConfigurationSelector继承了AdviceModeImportSelector类,那么我们在点开AdviceModeImportSelector类,看看有什么秘密:
public abstract class AdviceModeImportSelector<A extends Annotation> implements ImportSelector { ... } 复制代码
是不是很眼熟,没错,AdviceModeImportSelector实现了ImportSelector类,那么我们是不是可以推断,@EnableAysnc是以 “接口编程”
的方式实现@Enbale模块的。
2) 那么,接下来我们再举个栗子,看一下@EnableWebMvc注解有什么神秘之处
:
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Documented @Import({DelegatingWebMvcConfiguration.class}) public @interface EnableWebMvc { } 复制代码
以上源码相信小伙伴们已经猜到了, @EnableWebMvc
标注了 @Import({DelegatingWebMvcConfiguration.class})
。好,我们打开DelegatingWebMvcConfiguration看看源码是什么:
@Configuration public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport { ... } 复制代码
有没有看出什么特别之处呢?DelegatingWebMvcConfiguration类是一个@Configuration的类。所以我们一样可以推断,@EnableWebMvc是以 “注解驱动”
实现@Enbale模块的。
相信看到这里,不少小伙伴已经可以自定义@Enable模块驱动了。
前面提到过@Enable模块驱动使用 @Import
实现,并且@Import的职责在于装载导入类(Importing Class),将其定义为Spring Bean。导入类主要为 @Configuration Class
、 ImportSelector实现
以及 ImportBeanDefinitionRegistrar实现
。
1)装载@Configuration Class
<context:component-scan/>
。 <context:component-scan/>
结合使用,@Import的处理依旧无法执行。因此我们以前在开发spring项目时,同时配置XML元素 <context:component-scan/>
与 <context:annotation-config/>
。
下面我们分析 <context:annotation-config/>
所对应的 BeanDefinitionParser
的实现类 AnnotationConfigBeanDefinitionParser
:
public class AnnotationConfigBeanDefinitionParser implements BeanDefinitionParser { ... @Nullable public BeanDefinition parse(Element element, ParserContext parserContext) { Object source = parserContext.extractSource(element); Set<BeanDefinitionHolder> processorDefinitions = AnnotationConfigUtils.registerAnnotationConfigProcessors(parserContext.getRegistry(), source); ... return null; } } 复制代码
我们看源码得知, parse(Element,ParserContext)
方法调用 AnnotationConfigUtils#registerAnnotationConfigProcessors(BeanDefinitionRegistry,Object)
方法实现BeanDefinition( 用于存储Bean的定义信息
)的实例解析。
该方法从Spring Framework3.0开始,新增了@Configuration Class的处理实现 ConfigurationClassPostProcessor
:
public abstract class AnnotationConfigUtils { ... public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source) { ... Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet(8); RootBeanDefinition def; if (!registry.containsBeanDefinition("org.springframework.context.annotation.internalConfigurationAnnotationProcessor")) { def = new RootBeanDefinition(ConfigurationClassPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, "org.springframework.context.annotation.internalConfigurationAnnotationProcessor")); } ... return beanDefs; } } 复制代码
从源码我可以看出,ConfigurationClassPostProcessor被封装成Spring Bean定义(BeanDefinition),接着注册为Spring Bean,且Bean的名称为“org.springframework.context.annotation.internalConfigurationAnnotationProcessor”。
那么我们脑洞下,所以在Spring Framework中:
<context:annotation-config/>
的底层实现类AnnotationConfigBeanDefinitionParser调用了 AnnotationConfigUtils#registerAnnotationConfigProcessors(BeanDefinitionRegistry, Object)
方法注册ConfigurationClassPostProcessor Bean。 <context:component-scan/>
的底层实现类ComponentScanBeanDefinitionParser也调用了 AnnotationConfigUtils#registerAnnotationConfigProcessors(BeanDefinitionRegistry, Object)
方法注册ConfigurationClassPostProcessor Bean。
其实ConfigurationClassPostProcessor不仅在XML配置驱动下才可装配,Spring Framework3.0注解驱动上下文实现 AnnotationConfigApplicationContext
也可以装配:
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry { private final AnnotatedBeanDefinitionReader reader; ... public AnnotationConfigApplicationContext() { this.reader = new AnnotatedBeanDefinitionReader(this); ... } ... } 复制代码
我们继续观察成员变量AnnotatedBeanDefinitionReader的构造函数:
public class AnnotatedBeanDefinitionReader { ... public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) { this(registry, getOrCreateEnvironment(registry)); } public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) { ... this.registry = registry; ... AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); } } 复制代码
我们分析以上源码,可以发现 AnnotationConfigApplicationContext
的 AnnotatedBeanDefinitionReader
类型成员reader在构造时,也显式的调用了 AnnotationConfigUtils#registerAnnotationConfigProcessors(BeanDefinitionRegistry, Object)
方法。
在Spring应用上下文启动中(AbstractApplicationContext#refresh()方法被调用时),Spring容器(BeanFactory)将ConfigurationClassPostProcessor初始化为Spring Bean。它作为BeanFactoryPostProcessor实现,随后其postProcessBeanFactory(ConfigurableListableBeanFactory)方法被调用。
我们先来看看ConfigurationClassPostProcessor源码:
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor, ... { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { ... } } 复制代码
ConfigurationClassPostProcessor实现了BeanDefinitionRegistryPostProcessor,而后者继承了BeanFactoryPostProcessor
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor { ... } 复制代码
public interface BeanFactoryPostProcessor { void postProcessBeanFactory(ConfigurableListableBeanFactory var1) throws BeansException; } 复制代码
可以看出ConfigurationClassPostProcessor实现了BeanFactoryPostProcessor并重写了postProcessBeanFactory方法。postProcessBeanFactory方法被调用后,随之处理@Configuration类和@Bean方法:
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor, PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware { ... @Override 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)) { // BeanDefinitionRegistryPostProcessor hook apparently not supported... // Simply call processConfigurationClasses lazily at this point then. processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory); } enhanceConfigurationClasses(beanFactory); beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory)); } /** * Build and validate a configuration model based on the registry of * {@link Configuration} classes. */ public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); String[] candidateNames = registry.getBeanDefinitionNames(); for (String beanName : candidateNames) { BeanDefinition beanDef = registry.getBeanDefinition(beanName); if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) || ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) { if (logger.isDebugEnabled()) { logger.debug("Bean definition has already been processed as a configuration class: " + beanDef); } } else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } } // Return immediately if no @Configuration classes were found if (configCandidates.isEmpty()) { return; } // Sort by previously determined @Order value, if applicable configCandidates.sort((bd1, bd2) -> { int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); return Integer.compare(i1, i2); }); // Detect any custom bean name generation strategy supplied through the enclosing application context SingletonBeanRegistry sbr = null; if (registry instanceof SingletonBeanRegistry) { sbr = (SingletonBeanRegistry) registry; if (!this.localBeanNameGeneratorSet) { BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR); if (generator != null) { this.componentScanBeanNameGenerator = generator; this.importBeanNameGenerator = generator; } } } if (this.environment == null) { this.environment = new StandardEnvironment(); } // Parse each @Configuration class ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); do { parser.parse(candidates); parser.validate(); Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed); // Read the model and create bean definitions based on its content if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses); candidates.clear(); if (registry.getBeanDefinitionCount() > candidateNames.length) { String[] newCandidateNames = registry.getBeanDefinitionNames(); Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames)); Set<String> alreadyParsedClasses = new HashSet<>(); for (ConfigurationClass configurationClass : alreadyParsed) { alreadyParsedClasses.add(configurationClass.getMetadata().getClassName()); } for (String candidateName : newCandidateNames) { if (!oldCandidateNames.contains(candidateName)) { BeanDefinition bd = registry.getBeanDefinition(candidateName); if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) { candidates.add(new BeanDefinitionHolder(bd, candidateName)); } } } candidateNames = newCandidateNames; } } while (!candidates.isEmpty()); // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) { sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry()); } if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) { // Clear cache in externally provided MetadataReaderFactory; this is a no-op // for a shared cache since it'll be cleared by the ApplicationContext. ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache(); } } } 复制代码
2)装载ImportSelector和ImportBeanDefinitionRegistrar实现
由于ImportSelector和ImportBeanDefinitionRegistrar从Spring Frameeork3.1才开始引入,所以3.0版本中不会出现两者的实现。由于BeanDefinitionRegistryPostProcessor从Spring Framework3.0.1开始引入,ConfigurationClassPostprocessor的实现也随之发生变化,其实现接口从BeanFactoryPostProcessor替换为BeanDefinitionRegistryPostProcessor,且BeanDefinitionRegistryPostProcessor扩展了BeanFactoryPostProcessor接口,所以ConfigurationClassPostProcessor存在两个阶段:
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor, PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { ... } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { ... } } 复制代码
除两阶段实现外,ConfigurationClassPostProcessor在Spring Framework3.1中并没有太多变化,该版本的主要变化还是集中在ConfigurationClassParser的实现上,在其doProcessConfigurationClass(ConfigurationClass,AnnotationMetadata)方法中,增加了@PropertySource和@ComponentScan注解处理,并且更新了processImport(ConfigurationClass,String[],boolean)方法的实现:
class ConfigurationClassParser { protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { if (configClass.getMetadata().isAnnotated(Component.class.getName())) { // Recursively process any member (nested) classes first processMemberClasses(configClass, sourceClass); } // Process any @PropertySource annotations for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) { if (this.environment instanceof ConfigurableEnvironment) { processPropertySource(propertySource); } else { logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment"); } } // Process any @ComponentScan annotations Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { for (AnnotationAttributes componentScan : componentScans) { // The config class is annotated with @ComponentScan -> perform the scan immediately Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // Check the set of scanned definitions for any further config classes and parse recursively if needed for (BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); if (bdCand == null) { bdCand = holder.getBeanDefinition(); } if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } } // Process any @Import annotations processImports(configClass, sourceClass, getImports(sourceClass), true); // Process any @ImportResource annotations AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); if (importResource != null) { String[] resources = importResource.getStringArray("locations"); Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader"); for (String resource : resources) { String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); configClass.addImportedResource(resolvedResource, readerClass); } } // Process individual @Bean methods Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass); for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } // Process default methods on interfaces processInterfaces(configClass, sourceClass); // Process superclass, if any if (sourceClass.getMetadata().hasSuperClass()) { String superclass = sourceClass.getMetadata().getSuperClassName(); if (superclass != null && !superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) { this.knownSuperclasses.put(superclass, configClass); // Superclass found, return its annotation metadata and recurse return sourceClass.getSuperClass(); } } // No superclass -> processing is complete return null; } } 复制代码
class ConfigurationClassParser { ... private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, 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)) { // Candidate class is an ImportSelector -> delegate to it to determine imports Class<?> 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 definitions Class<?> 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 class this.importStack.registerImport( currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); processConfigurationClass(candidate.asConfigClass(configClass)); } } } 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(); } } } } 复制代码
通过AssignabletypeFilter判断候选Class元注解@Import是否赋值ImportSelector或ImportBeanDefinitionRegistrar实现,从而决定是否执行ImportSelector或ImportBeanDefinitionRegistrar处理,其中importingClassMetadata就是当前元注解@Import的AnnotationMetadata对象。
综上所述,ConfigurationClassPostProcessor负责筛选@Component Class、@Configuration Class及@Bean方法的Bean定义(BeanDefinition),ConfigurationClassPrser则从候选的Bean定义中解析除Configuration集合,随后被ConfigurationClassBeanDefinitionReader转化并注册BeanDefinition。
@Enable “模块驱动”有两种实现方式
Spring Framework装载 @Configuration Class
、 ImportSelector实现
以及 ImportBeanDefinitionRegistrar实现
时,需引入@Import或@ComponentScan。
但Spring Framework并不具备自动装配的能力。下节将说说SpringBoot自动装配。
【参考小马哥SpringBoot编程思想】