springboot2.x basic tutorial: @Enable* principle

The previous springboot 2.x basic tutorial: @Async opens asynchronous tasks. We used the @EnableAsync annotation to enable asynchronous execution.
There are many @Enable* annotations in the SpringBoot framework, such as: @EnableAspectJAutoProxy, @EnableCaching, @EnableAutoConfiguration, @EnableSwagger2 This chapter explains the principle behind it.

Several typical @Enable* annotations

The three annotations @EnableSwagger2, @EnableAsync, and @EnableAspectJAutoProxy are posted below. It is not difficult to see that these three annotations all use the @Import annotation.
@Import annotation supports importing common java classes and declaring them as a bean.
The actual @Enable annotation is to inject Bean into the springboot ioc container through the ability of @Import annotation to make certain configurations take effect.

@EnableScheduling

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import({SchedulingConfiguration.class})
@Documented
public @interface EnableScheduling {
}

@EnableAsync

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AsyncConfigurationSelector.class})
public @interface EnableAsync {
    Class<? extends Annotation> annotation() default Annotation.class;
    boolean proxyTargetClass() default false;
    AdviceMode mode() default AdviceMode.PROXY;
    int order() default 2147483647;
}

@EnableAspectJAutoProxy

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AspectJAutoProxyRegistrar.class})
public @interface EnableAspectJAutoProxy {
    boolean proxyTargetClass() default false;

    boolean exposeProxy() default false;
}

@Import annotation usage

Allow @Configuration annotated classes

The @Import annotated by @EnableScheduling imports the SchedulingConfiguration class. Let's look at the implementation of this class.
We can see that this class uses @Configuration and @Bean annotations.
@Import annotation allows classes annotated with @Configuration to be injected into the container.

@Configuration
@Role(2)
public class SchedulingConfiguration {
    public SchedulingConfiguration() {
    }
    @Bean(
        name = {"org.springframework.context.annotation.internalScheduledAnnotationProcessor"}
    )
    @Role(2)
    public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
        return new ScheduledAnnotationBeanPostProcessor();
    }
}

Allows the use of classes that implement the ImportSelectorj interface

@EnableAsync annotated @Import imports the AsyncConfigurationSelector class, let’s look at the implementation of this class.
The AsyncConfigurationSelector class implements the ImportSelector interface and overrides the selectImports method.
@Import annotation here will inject the full path class returned by selectImports into the container.

public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
    private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME = "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";

    public AsyncConfigurationSelector() {
    }

    @Nullable
    public String[] selectImports(AdviceMode adviceMode) {
        switch(adviceMode) {
        case PROXY:
            return new String[]{ProxyAsyncConfiguration.class.getName()};
        case ASPECTJ:
            return new String[]{"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration"};
        default:
            return null;
        }
    }
}

Allowed is a class that implements the ImportBeanDefinitionRegistrar interface

@EnableAspectJAutoProxy annotated @Import imports the AspectJAutoProxyRegistrar class, let’s look at the implementation of this class.
AspectJAutoProxyRegistrar implements the ImportBeanDefinitionRegistrar interface. The role of ImportBeanDefinitionRegistrar is to automatically add Beans to the existing configuration classes at runtime, and to parse and generate beans when the Spring container starts, by rewriting the method registerBeanDefinitions.

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    AspectJAutoProxyRegistrar() {
    }

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
        AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
        if (enableAspectJAutoProxy != null) {
            if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            }

            if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
            }
        }
    }
}

@Import annotation SpringBoot processing flow source code analysis

  1. The refresh method of AbstractApplicationContext is called when the Spring IOC container is initialized
  2. Various BeanFactoryPostProcessor will be called in refresh. The processConfigBeanDefinitions method of ConfigurationClassPostProcessor handles annotations such as @Configuration and @Import.
  3. ConfigurationClassPostProcessor is actually processed internally by ConfigurationClassParser.
  4. Let's focus on the @Import handling of the ConfigurationClassParser class.

ConfigurationClassParser class source code analysis

The core processing code is in the processImports method of ConfigurationClassParser, which implements the processing of the three ways of using @Import in this article.

  private void processImports(ConfigurationClass configClass, ConfigurationClassParser.SourceClass currentSourceClass, Collection<ConfigurationClassParser.SourceClass> importCandidates, Predicate<String> exclusionFilter, boolean checkForCircularImports) {
	//....省略部分代码
                        ConfigurationClassParser.SourceClass candidate = (ConfigurationClassParser.SourceClass)var6.next();
                        Class candidateClass;
                        if (candidate.isAssignable(ImportSelector.class)) {
							//对于处理ImportSelector注解处理
                            candidateClass = candidate.loadClass();
                            ImportSelector selector = (ImportSelector)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<ConfigurationClassParser.SourceClass> importSourceClasses = this.asSourceClasses(importClassNames, exclusionFilter);
                                this.processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
                            }
                        } else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
							//对于ImportBeanDefinitionRegistrar接口处理
                            candidateClass = candidate.loadClass();
                            ImportBeanDefinitionRegistrar registrar = (ImportBeanDefinitionRegistrar)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());
							//对于@Configuration注解处理
                            this.processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
                        }
	//....省略部分代码
    }

A thousand miles begins with a single step. This is the eleventh article of the SpringBoot tutorial series. All project source codes can be downloaded on my GitHub .

Guess you like

Origin blog.csdn.net/github_35592621/article/details/108248929