springboot啓動流程(六)ioc容器刷新前prepareContext

全部文章

http://www.javashuo.com/article/p-uvudtich-bm.htmlhtml

 

prepareContext方法核心邏輯

上一篇文章中,咱們經過createApplicationContext方法建立了一個ApplicationContext的實例對象。本文將閱讀一下在ApplicationContext在refresh以前的prepareContext中作了哪些事情。spring

 

咱們跟進prepareContext方法springboot

private void prepareContext(
        ConfigurableApplicationContext context, 
        ConfigurableEnvironment environment,
        SpringApplicationRunListeners listeners, 
        ApplicationArguments applicationArguments, 
        Banner printedBanner
        ) {
    // 設置Environment
    context.setEnvironment(environment);
    // 後置處理
    postProcessApplicationContext(context);
    // 調用ApplicationContextInitializer接口的實現
    applyInitializers(context);
    // 發佈ApplicationContext準備事件
    listeners.contextPrepared(context);
    
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    // 註冊args參數爲單例bean
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
        // 註冊banner爲單例bean
        beanFactory.registerSingleton("springBootBanner", printedBanner);
    }
    if (beanFactory instanceof DefaultListableBeanFactory) {
        // 設置beanFactory中是否容許重複bean覆蓋
        ((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }

    // 加載main方法所在類
    Set<Object> sources = getAllSources();
    // 註冊main方法所在類到beanFactory
    load(context, sources.toArray(new Object[0]));
    // 發佈Context加載事件
    listeners.contextLoaded(context);
}

咱們看到prepareContext方法主要邏輯包含了三塊內容app

1)基本的初始化,如設置Environment,調用ApplicationContextInitializer接口的實現類post

2)註冊現有的對象爲單例bean,如args、bannerthis

3)加載main方法所在的主類spa

 

load方法加載主類

很顯然,加載main方法的主類是咱們關注的重點。咱們先看看getAllSources方法返回code

public Set<Object> getAllSources() {
    Set<Object> allSources = new LinkedHashSet<>();
    // 添加主類
    if (!CollectionUtils.isEmpty(this.primarySources)) {
        allSources.addAll(this.primarySources);
    }
    // 添加附加資源類
    if (!CollectionUtils.isEmpty(this.sources)) {
        allSources.addAll(this.sources);
    }
    return Collections.unmodifiableSet(allSources);
}

primarySources是在SpringApplication初始化的時候設置的,而sources默認是沒有的。所在getAllSoures方法將返回main方法所在的主類。xml

 

下面,咱們跟進load方法,閱讀一下加載main所在主類的邏輯htm

protected void load(ApplicationContext context, Object[] sources) {
    // 獲取BeanDefinition加載器
    BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
    if (this.beanNameGenerator != null) {
        loader.setBeanNameGenerator(this.beanNameGenerator);
    }
    if (this.resourceLoader != null) {
        loader.setResourceLoader(this.resourceLoader);
    }
    if (this.environment != null) {
        loader.setEnvironment(this.environment);
    }
    // 加載資源
    loader.load();
}

load方法中,先是得到了BeanDefinitionLoader,而後去加載資源。這裏要注意!爲何是BeanDefinitionLoader呢?

首先,咱們得知道Spring的bean資源來自各類地方,如xml、annotation等,那麼這些bean在配置的時候也就是對bean進行定義,而這些定義映射到內存中的對象就是BeanDefinition的對象,Spring後續會根據BeanDefinition再獲取具體的bean。

簡單來講就是:配置 --> BeanDefinition --> Bean 這樣一個邏輯

因此,後續咱們會看到BeanDefinitionLoader會將主類加載成BeanDefinition,而後註冊到ApplicationContext當中。

 

先跟進getBeanDefinitionRegistry方法,看看BeanDefinition會被註冊到哪裏去

private BeanDefinitionRegistry getBeanDefinitionRegistry(ApplicationContext context) {
    // 返回當前ApplicationContext
    if (context instanceof BeanDefinitionRegistry) {
        return (BeanDefinitionRegistry) context;
    }
    if (context instanceof AbstractApplicationContext) {
        return (BeanDefinitionRegistry) ((AbstractApplicationContext) context).getBeanFactory();
    }
    throw new IllegalStateException("Could not locate BeanDefinitionRegistry");
}

咱們注意,springboot的AnnotationConfigServletWebServerApplicationContext這個ApplicationContext的實現類,隨着繼承鏈路向上走是繼承自GenericApplicationContext的,而GenericApplicationContext實現了BeanDefinitionRegistry。

因此,getBeanDefinitionRegistry將最終返回強轉過的ApplicationContext。也就是說BeanDefinition將被註冊到ApplicationContext裏面。

 

回到load方法中,咱們再跟進createBeanDefinitionLoader方法

protected BeanDefinitionLoader createBeanDefinitionLoader(BeanDefinitionRegistry registry, Object[] sources) {
    return new BeanDefinitionLoader(registry, sources);
}

再跟進構造方法

BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
    this.sources = sources;
    // 註解方式的讀取器
    this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
    // xml方式的讀取器
    this.xmlReader = new XmlBeanDefinitionReader(registry);

    // 類路徑下的掃描器
    this.scanner = new ClassPathBeanDefinitionScanner(registry);
    // 掃描排除當前main方法的主類
    this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
}

咱們看到加載器支持註解、xml兩種方式。類路徑下的掃描器排除了當前的主類

 

回到load方法

protected void load(ApplicationContext context, Object[] sources) {
    // 獲取BeanDefinition加載器
    BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
    if (this.beanNameGenerator != null) {
        loader.setBeanNameGenerator(this.beanNameGenerator);
    }
    if (this.resourceLoader != null) {
        loader.setResourceLoader(this.resourceLoader);
    }
    if (this.environment != null) {
        loader.setEnvironment(this.environment);
    }
    // 加載資源
    loader.load();
}

此時,咱們已經獲取了BeanDefinitionLoader,下面調用該loader的load方法開始加載

 

跟進第二個load方法

public int load() {
    int count = 0;
    for (Object source : this.sources) {
        count += load(source);
    }
    return count;
}

再跟進第三個load方法

private int load(Object source) {
    if (source instanceof Class<?>) {
        return load((Class<?>) source);
    }
    if (source instanceof Resource) {
        return load((Resource) source);
    }
    if (source instanceof Package) {
        return load((Package) source);
    }
    if (source instanceof CharSequence) {
        return load((CharSequence) source);
    }
    throw new IllegalArgumentException("Invalid source type " + source.getClass());
}

因爲咱們的主類是一個class,因此進入第一個if分支的load方法

 

繼續跟進

private int load(Class<?> source) {
    // 省略
    if (isComponent(source)) {
        this.annotatedReader.register(source);
        return 1;
    }
    return 0;
}

該方法先經過isComponent方法判斷了主類是否是被@Component註解,若是是,那麼調用註解方式的閱讀器,註冊該資源。

 

跟進isComponent方法,看看怎麼判斷的

private boolean isComponent(Class<?> type) {
    // 找到是否匹配@Component註解
    if (AnnotationUtils.findAnnotation(type, Component.class) != null) {
        return true;
    }
    // 省略
}

其實就是找這個類是否有@Component註解,但請注意咱們一般都使用@SpringBootApplication這個註解,並無直接註解@Component。而@SpringBootApplication是一個組合註解,其中就組合了@Component

而AnnotationUtils.findAnnotation方法將會遞歸遍歷註解,最終找到@Component。

 

isComponent判斷爲true之後,咱們再跟進annotationReader.register(source)閱讀一下讀取主類的過程

public void register(Class<?>... annotatedClasses) {
    for (Class<?> annotatedClass : annotatedClasses) {
        registerBean(annotatedClass);
    }
}

繼續跟進registerBean方法

public void registerBean(Class<?> annotatedClass) {
    doRegisterBean(annotatedClass, null, null, null);
}

再跟進doRegisterBean方法,該方法比較長,咱們省略掉一些次要的部分

<T> void doRegisterBean(
        Class<T> annotatedClass, 
        @Nullable Supplier<T> instanceSupplier, 
        @Nullable String name,
        @Nullable Class<? extends Annotation>[] qualifiers, 
        BeanDefinitionCustomizer... definitionCustomizers
        ) {
    // 先包裝成BeanDefinition
    AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
    
    // 解析scope元數據
    ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
    abd.setScope(scopeMetadata.getScopeName());

    // 生成bean的名
    String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));

    // 解析一些常見的註解元數據
    AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);

    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    // 註冊BeanDefinition到ApplicationContext
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}

能夠看到,doRegisterBean方法的主要邏輯就是包裝並解析出一個BeanDefinition,而後調用registerBeanDefinition方法把BeanDefinition給註冊到ApplicationContext中。

註冊相關的本文就再也不繼續展開了,後續的文章會跟進這些內容。

 

總結

總的來講,prepareContext方法主要就是爲了加載並註冊主類的BeanDefinition到ApplicationContext。這裏注意!咱們一直都在說註冊到ApplicationContext,但熟悉spring的都會知道不管是Bean仍是BeanDefinition都是註冊到BeanFactory中的。但咱們一直沒有嚴格區分它,後續的文章咱們將會把ApplicationContext和BeanFactory進行區分。

相關文章
相關標籤/搜索