明天来说说 @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 | 服务熔断模块 |
- 引入 @Enable 模块驱动的意义在于能
简化拆卸步骤
,实现了“按需拆卸”
,同时屏蔽组件汇合拆卸的细节。
1、分析 @Enable“模块驱动”
这就要从 Spring Framework3.0 新引入的 @Import 说起。@Import 用于导入一个或多个 ConfigurationClass,将其注册为 Spring Bean。这里须要留神的是:
Spring Framework3.0 中存在肯定的限度,仅反对 @Configuration 标注的类
在 Spring Framework3.1 中,@Import 则扩充了指摘范畴,还能够用于申明至多一个 @Bean 办法的类,以及 ImportSelector 或 ImportBeanDefinitionRegistrar 的实现类
预计有小伙伴曾经发现了,Spring Framework 提供了 两类 对于 @Enable 模块驱动的实现形式:
- 被 @Configuration 类和 @Bean 办法申明的类归类为
“注解驱动”
。 - 以 ImportSelector 或 ImportBeanDefinitionRegistrar 的实现类归类为
“接口编程”
。
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 模块驱动了。
2、@Enable 模块驱动原理
后面提到过 @Enable 模块驱动应用 @Import
实现,并且 @Import 的职责在于装载导入类(Importing Class), 将其定义为 Spring Bean。导入类次要为 @Configuration Class
、ImportSelector 实现
以及ImportBeanDefinitionRegistrar 实现
。
1)装载 @Configuration Class
- @Configuration 是 Spring Framework3.0 开始引入的,但该版本还未引入 @ComponentScan 注解驱动,因而配套的导入注解是 @Import。
- 只管 Spring Framework3.0 提供了注解驱动上下文实现 AnnotationConfigApplicationContext,但与 @Import 配合比拟繁琐,仅反对 Spring 组件类的一一导入,如这样 @Import({A.class,B.class,…}),因而过后还是无奈齐全代替 XML 元素
<context:component-scan/>
。 - 即便 Spring 利用上下文与
<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 主动拆卸。