明天来说说@Enable模块驱动。Spring Framework是从3.1版本开始反对“@Enable模块驱动”的。所谓“模块”是指具备雷同畛域的性能组件汇合,组合造成一个独立的单元。

图表是Spring FrameworkSpring Bootspring Cloud的@Enable注解模块:

框架实现@Enable注解模块激活模块
Spring Framework@EnableWebMvcWeb Mvc模块
@EnableTransactionManagement事务管理模块
@EnableCachingCaching模块
@EnableMBeanExportJMX模块
@EnableAsync异步解决模块
@EnableWebFluxWeb Flux 模块
@EnableAspectJAutoProxyAspectJ模块
Spring Boot@EnableAutoConfiguration主动拆卸模块
@EnableManagementContextActuator模块
@EnableConfigurationProperties配置属性绑定模块
@EnableOAuth2SsoOAuth2 单点登陆模块
spring Cloud@EnableEurekaServerEureka服务模块
@EnableConfigServer配置服务器模块
@EnableFeignCliensFeign客户端模块
@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看看源码是什么:

@Configurationpublic class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {    ...}

有没有看出什么特别之处呢?DelegatingWebMvcConfiguration类是一个@Configuration的类。所以咱们一样能够推断,@EnableWebMvc是以“注解驱动”实现@Enbale模块的。

置信看到这里,不少小伙伴曾经能够自定义@Enable模块驱动了。

2、@Enable模块驱动原理

后面提到过@Enable模块驱动应用@Import实现,并且@Import的职责在于装载导入类(Importing Class),将其定义为Spring Bean。导入类次要为@Configuration ClassImportSelector实现以及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);    }}

咱们剖析以上源码,能够发现AnnotationConfigApplicationContextAnnotatedBeanDefinitionReader类型成员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 ClassImportSelector实现以及ImportBeanDefinitionRegistrar实现时,需引入@Import或@ComponentScan。

但Spring Framework并不具备主动拆卸的能力。下节将说说SpringBoot主动拆卸。