以前のspringboot 2.x基本チュートリアル:@Asyncは非同期タスクを開くため、@ EnableAsyncアノテーションを使用して非同期実行を有効にしました。
SpringBootフレームワークには、@ EnableAspectJAutoProxy、@ EnableCaching、@ EnableAutoConfiguration、@ EnableSwagger2など、多くの@ Enable *アノテーションがあります。この章では、その背後にある原理について説明します。
この記事のナビゲーション
いくつかの典型的な@ Enable *アノテーション
@ EnableSwagger2、@ EnableAsync、@ EnableAspectJAutoProxyの3つのアノテーションを以下に掲載しますが、これらの3つのアノテーションがすべて@Importアノテーションを使用していることを確認するのは難しくありません。
@Importアノテーションは、一般的なJavaクラスのインポートとBeanとしての宣言をサポートしています。
実際の@Enableアノテーションは、@ Importアノテーションの機能を介してBeanをspringboot iocコンテナに注入し、特定の構成を有効にします。
@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アノテーションの使用法
@Configuration注釈付きクラスを許可する
@EnableSchedulingによって注釈が付けられた@ImportはSchedulingConfigurationクラスをインポートしますこのクラスの実装を見てみましょう。
このクラスは@Configurationおよび@Beanアノテーションを使用していることがわかります。
@Importアノテーションを使用すると、@ Configurationアノテーションが付けられたクラスをコンテナに注入できます。
@Configuration
@Role(2)
public class SchedulingConfiguration {
public SchedulingConfiguration() {
}
@Bean(
name = {"org.springframework.context.annotation.internalScheduledAnnotationProcessor"}
)
@Role(2)
public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
return new ScheduledAnnotationBeanPostProcessor();
}
}
ImportSelectorjインターフェースを実装するクラスの使用を許可します
@EnableAsyncアノテーション付き@ImportはAsyncConfigurationSelectorクラスをインポートします。このクラスの実装を見てみましょう。
AsyncConfigurationSelectorクラスはImportSelectorインターフェイスを実装し、selectImportsメソッドをオーバーライドします。
ここの@Importアノテーションは、selectImportsによって返されたフルパスクラスをコンテナに注入します。
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は、ImportBeanDefinitionRegistrarインターフェースを実装するクラスです。
@EnableAspectJAutoProxyアノテーション付き@Importは、AspectJAutoProxyRegistrarクラスをインポートします。このクラスの実装を見てみましょう。
AspectJAutoProxyRegistrarはImportBeanDefinitionRegistrarインターフェースを実装します。ImportBeanDefinitionRegistrarの役割は、実行時に既存の構成クラスにBeanを自動的に追加し、メソッドregisterBeanDefinitionsを書き換えて、Springコンテナの起動時にBeanを解析および生成することです。
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アノテーションSpringBoot処理フローのソースコード分析
- Spring IOCコンテナが初期化されると、AbstractApplicationContextの更新メソッドが呼び出されます
- さまざまなBeanFactoryPostProcessorが更新時に呼び出されます。ConfigurationClassPostProcessorのprocessConfigBeanDefinitionsメソッドは、@ Configurationや@Importなどの注釈を処理します。
- ConfigurationClassPostProcessorは、実際にはConfigurationClassParserによって内部的に処理されます。
- ConfigurationClassParserクラスの@Import処理に注目しましょう。
ConfigurationClassParserクラスのソースコード分析
コア処理コードはConfigurationClassParserのprocessImportsメソッドにあり、この記事で@Importを使用する3つの方法の処理を実装しています。
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);
}
//....省略部分代码
}
千マイルは単一のステップから始まります。これは、SpringBootチュートリアルシリーズの11番目の記事で、すべてのプロジェクトのソースコードはGitHubからダウンロードできます。