動態

詳情 返回 返回

《底層到底做了什麼》--- 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 lslove 頭像 souyunku 頭像 debuginn 頭像 ssbunny 頭像 decaday 頭像 lenve 頭像 gupan 頭像 tuhooo 頭像 icodewalker 頭像 edagarli 頭像 xiaoyongyong 頭像 changqingdezi 頭像
點贊 13 用戶, 點贊了這篇動態!
點贊

Add a new 評論

Some HTML is okay.