動態

詳情 返回 返回

《底層到底做了什麼》--- spring 的 @EnableAsync

在spring 框架中開啓某項功能,常常會在Application類中使用@EnableXXX註解,來開啓某個功能。“開啓”其實就是把相關的bean注入spring容器中。

以@EnableAsync為例。在@EnableAsync中使用@import把相關的bean注入容器中,這個bean默認是ProxyAsyncConfiguration。ProxyAsyncConfiguration是個@Configuration,導入AsyncAnnotationBeanPostProcessor,這是實際Async功能使用的bean。

再舉一個例子,在spring中起到自動化配置的@EnableConfigurationProperties,也是通過@import注入 EnableConfigurationPropertiesRegistrar,通過後者掃描加載配置。

下面將説明@EnableAsync在spring底層的執行流程。通過這個例子,也能看到spring中最基礎的功能 IOC和AOP 的底層執行過程。

先看一個常規的demo
(説明在前,代碼在後)

1、 在ApiApplication上聲明 @EnableAsync

@SpringBootApplication
@EnableConfigurationProperties
@EnableAsync  //1
public class ApiApplication {
  public static void main(String[] args) { 
    SpringApplication.run(ApiApplication.class, args);
  }
}

2、在@EnableAsync裏,使用@Import聲明,通過AsyncConfigurationSelector導入需要的bean。


@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AsyncConfigurationSelector.class})//2
public @interface EnableAsync {
    //省略
}

3、selectImports()返回要構造bean,spring會根據返回值實際去構造bean。 selector的意義就在於根據參數選擇要裝配的bean。


public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> { 
//省略

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

以上是業務層看到的代碼,但是看不到@EnableAsync什麼時候被解析,selectImports什麼時候被調用,bean怎麼被構造和裝配。

下面深入底層代碼看執行流程

1、還是相同的入口


public class ApiApplication {
  public static void main(String[] args) { 
    SpringApplication.run(ApiApplication.class, args);
  }
} 

2、調用SpringApplication的run方法。


public class SpringApplication {
    public ConfigurableApplicationContext run(String... args) {
    //省略
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            this.refreshContext(context);//2
            this.afterRefresh(context, applicationArguments);
    //省略 
  }

3、 調用SpringApplication的refresh方法。


    protected void refresh(ConfigurableApplicationContext applicationContext) {
        applicationContext.refresh();//3
    }
} 

4、調用AbstractApplicationContext的refresh。


public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
   
    public void refresh() throws BeansException, IllegalStateException {

    //省略
    this.prepareRefresh();
    this.postProcessBeanFactory(beanFactory);
    this.invokeBeanFactoryPostProcessors(beanFactory);//4
    this.registerBeanPostProcessors(beanFactory);
    //省略
   }
}

5、調用PostProcessorRegistrationDelegate 的invokeBeanDefinitionRegistryPostProcessors方法。

final class PostProcessorRegistrationDelegate {
private static void invokeBeanDefinitionRegistryPostProcessors(Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {
        Iterator var2 = postProcessors.iterator();

        while(var2.hasNext()) {
            BeanDefinitionRegistryPostProcessor postProcessor = (BeanDefinitionRegistryPostProcessor)var2.next();
            postProcessor.postProcessBeanDefinitionRegistry(registry);//5
        }

    }
 }

中間部分調用代碼省略。

7、調用ConfigurationClassParser類的parse()方法。這裏beanName代表ApiApplication,metadata代表ApiApplication上所有的註解,包括@EnableAsync。

    protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
        this.processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
    }

8、調用ConfigurationClassParser的doProcessConfigurationClass()方法。其中的getImports()方法,參數sourceClass是ApiApplication.class,這個方法掃描ApiApplication上所有的annotation,返回@import聲明的bean名稱列表。具體實現是以ApiApplication為root,遞歸的掃描過程所有的@註解。

    protected final ConfigurationClassParser.SourceClass doProcessConfigurationClass(ConfigurationClass configClass, ConfigurationClassParser.SourceClass sourceClass, Predicate<String> filter) throws IOException {
         this.processImports(configClass, sourceClass, this.getImports(sourceClass), filter, true);//8
     }

9、調用ConfigurationClassParser的processImports方法,用來處理@import聲明的bean。輸入參數importCandidates是ApiApplication上@import聲明的bean名稱列表,包括AsyncConfigurationSelector。這個方法,逐個處理@import聲明的bean,當處理到AsyncConfigurationSelector時,先通過構造器生成AsyncConfigurationSelector對象,然後調用Selector的selectImports方法,返回ProxyAsyncConfiguration類名。

10、此時importSourceClasses為ProxyAsyncConfiguration類名。繼續遞歸processImports。


    private void processImports(ConfigurationClass configClass, ConfigurationClassParser.SourceClass currentSourceClass, Collection<ConfigurationClassParser.SourceClass> importCandidates, Predicate<String> exclusionFilter, boolean checkForCircularImports) { 
//省略
 Iterator var6 = importCandidates.iterator();

 while(var6.hasNext()) {
//省略
         String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());//9

         Collection<ConfigurationClassParser.SourceClass> importSourceClasses = this.asSourceClasses(importClassNames, exclusionFilter);
         this.processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);//10
}
//省略

11、如果importSourceClasses是Selector,將繼續遞歸獲得@import的bean。這裏ProxyAsyncConfiguration是個普通的bean,所以直接構造bean。

12、與第8步相同,繼續遞歸的檢查ProxyAsyncConfiguration上的@import聲明。

 if (candidate.isAssignable(ImportSelector.class)) {
//省略
 } else {
    this.importStack.registerImport(currentSourceClass.getMetadata(),     candidate.getMetadata().getClassName());//11
    this.processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);//12

}
//省略

到此,ProxyAsyncConfiguration解析完成。下面創建ProxyAsyncConfiguration bean並初始化

1、前面的解析過程都是在1中執行,實際創建bean在2中。


public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
   
    public void refresh() throws BeansException, IllegalStateException {

    //省略
    this.prepareRefresh();
    this.postProcessBeanFactory(beanFactory);
    this.invokeBeanFactoryPostProcessors(beanFactory);//1
    this.registerBeanPostProcessors(beanFactory);//2
    //省略
   }
}

2、在這個方法裏創建bean
3、初始化bean,包括觸發bean生成後的回調方法。ProxyAsyncConfiguration實現了ImportAware接口,這裏觸發setImportMetadata方法。
4、將生成的bean放入容器,也就是IOC的核心步驟。


public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {

    protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
            instanceWrapper = this.createBeanInstance(beanName, mbd, args);//2

            this.populateBean(beanName, mbd, instanceWrapper);//6
            exposedObject = this.initializeBean(beanName, exposedObject, mbd);//3

 protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {

            this.autowireByName(beanName, mbd, bw, newPvs); //8

            pvsToUse = ibp.postProcessPropertyValues((PropertyValues)pvs, filteredPds, bw.getWrappedInstance(), beanName);//4

5、創建ProxyAsyncConfiguration對象。
6、其中delegate封裝了CGLIB的構造器,實際生成proxyAsyncConfiguration對象。


public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {

protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mbd) {
            beanInstance = this.getInstantiationStrategy().instantiate(mbd, beanName, this);
            }//5

            BeanWrapper bw = new BeanWrapperImpl(beanInstance); 
            this.initBeanWrapper(bw);
            return bw;
   }
}
class DelegatingConstructorAccessorImpl extends ConstructorAccessorImpl {
    private ConstructorAccessorImpl delegate;

    public Object newInstance(Object[] var1) throws InstantiationException, IllegalArgumentException, InvocationTargetException {
        return this.delegate.newInstance(var1);//6
    }
 
}

7、ProxyAsyncConfiguration裏定義了@Bean(AsyncAnnotationBeanPostProcessor),在構造的構成中也完成了AsyncAnnotationBeanPostProcessor的構造。

到此ProxyAsyncConfiguration bean的構造及初始化。

user avatar
0 用戶, 點贊了這篇動態!

發表 評論

Some HTML is okay.