spring最核心的東西-spring容器初始化到bean初始化的全過程

引伸: 其實一開始不想寫這篇文章的 , 奈何發現市面上的文章大部分都是隻知其一;不知其二 . 索性整理一篇文章方便之後閱讀查閱

此次就不一開始就長篇大論了 , 先說一個問題 ,爲啥咱們要研究spring 中bean的初始化順序java

爲啥研究spring中javabean的初始化順序

spring 框架是個啥? -> 本質上是一個IOC框架,解決依賴問題的玩意 , 依賴問題的核心承載點是啥? 是對象javabean , 因此若是把這一塊搞明白了,其實整個spring框架已經差很少搞透徹了.spring

spring中bean究竟是咋初始化的啊?

其實扒開spring的外衣 , 其實最核心的東西是BeanFactory , spring本身能找到的全部的java bean 全在裏面 , 這個就是整個spring的數據中心緩存

若是讓我說spring最大的成就是什麼 , 我認爲spring最大的成就就是實現整個容器中java對象邏輯的託管功能 , 也就是我認爲的IOC

spring容器初始化的開端ApplicationContext

這個是spring整個java對象管理運行的開端,我稱它爲始祖種子類

注意永遠記住spring是一個龐大臃腫功能強大的java框架 , 因此ApplicationContext的實現類特多,挑幾個重點來講多線程

  1. spring boot 中使用的ApplicationContext -> AnnotationConfigApplicationContext -> 使用java配置來實現將javabean信息注入到容器
  2. spring中最經常使用的ApplicationContext -> ClassPathXmlApplicationContext -> 使用xml來將javabean信息注入到容器
這兩個類都有繼承自AbstractApplication 全部的spring對象初始化的時候都會調用這個通用的類 , 整個bean初始化,容器加載相關的邏輯都是在這裏面的

spring IOC過程當中咱們最應該關注什麼問題

我認爲整個IOC過程當中最應該關注的就是這兩個問題 1. bean何時初始化的? 怎麼初始化的? 2. bean之間的依賴關係是何時? 是怎麼解決的? 3. 特殊狀態的javabean好比lookup和lazy是如何作的

bean何時初始化的

spring中bean的初始化的過程 , 我認爲分爲以下的幾個過程app

  1. java bean 加載進容器 , 而且根據類型進行必定的初始化
  2. 處理 java bean的依賴關係
這裏梳理一下他們的過程

javabean加載進容器, 而且根據類型進行必定的初始化操做

這一步中 ClassPathXMLApplicationContext和 AnnotationConfigApplicationContext的操做是不一樣的

1. ClassPathXMLApplication

ClassPathApplicationContext 在spring中屬於元老了 , 他的着一塊的支持是在AbstractApplicationContext中的refresh()方法(下面會給出源碼)中實現的

最核心的邏輯是在ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();這一行中框架

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    refreshBeanFactory();
    return getBeanFactory();
}

這裏其實知道結論就行了 ,沒有必要追究細節 , 這裏spring將會經過spring的xml文件生成一系列的javabean的包裝bean -> BeanDefinition ,
而後放入默認生成的DefaultListableBeanFactory之中, 注意在spring中 全部的java bean在解析的過程當中都會變成 BeanDefinition(有些特殊的單例不是用BeanDefinition封裝的)ide

2. AnnotionAppicationContext

這個須要重點關注一下 , 這個context和classPathXmLApplicationContext有必定的區別 , 他是繼承自GenericApplicationContext , GenericApplicationContext是新一代spring使用註解進行bean配置的核心Context

首先注意下這個ApplicationContext的構造方法函數

AnnotatedBeanDefinitionReader 和 ClassPathBeanDefinitionScanner 都是初始化用來將使用class或者basePackages名稱加載的BeanDefinition註冊進GenericApplicationContext的工具類工具

//將指定的java bean的配置類對象
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
    this();
    register(componentClasses);
    refresh();
}
//將制定的包下全部的配置類對象
public AnnotationConfigApplicationContext(String... basePackages) {
    this();
    scan(basePackages);
    refresh();
}
//初始化註冊BeanDefinition所須要的加載器 -> 這兩個方法很重要的 , 初始化了不少process好比AutowiredAnnotationBeanPostProcessor和
// CommonAnnotationBeanPostProcessor用來處理@Autowrite註解等等
public AnnotationConfigApplicationContext() {
    this.reader = new AnnotatedBeanDefinitionReader(this);
    this.scanner = new ClassPathBeanDefinitionScanner(this);
}
最核心的方法AbstractAplication的refresh()方法 , 這個方法完成了整個spring聲明週期的初始化
@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 準備,記錄容器的啓動時間startupDate, 標記容器爲激活,初始化上下文環境如文件路徑信息,驗證必填屬性是否填寫
        prepareRefresh();
        // 獲取新的beanFactory,銷燬原有beanFactory、爲每一個bean生成BeanDefinition等
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // 初始化beanfactory的各類屬性
        prepareBeanFactory(beanFactory);
        try {
            // 模板方法,此時,全部的beanDefinition已經加載,可是尚未實例化。
            //容許在子類中對beanFactory進行擴展處理。好比添加ware相關接口自動裝配設置,添加後置處理器等,是子類擴展prepareBeanFactory(beanFactory)的方法
            postProcessBeanFactory(beanFactory);
            // 實例化並調用全部註冊的beanFactory後置處理器(實現接口BeanFactoryPostProcessor的bean,在beanFactory標準初始化以後執行)
            invokeBeanFactoryPostProcessors(beanFactory);
            // 實例化和註冊beanFactory中擴展了BeanPostProcessor的bean
            //例如:
            // AutowiredAnnotationBeanPostProcessor(處理被@Autowired註解修飾的bean並注入)
            // RequiredAnnotationBeanPostProcessor(處理被@Required註解修飾的方法)
            // CommonAnnotationBeanPostProcessor(處理@PreDestroy、@PostConstruct、@Resource等多個註解的做用)等。
            registerBeanPostProcessors(beanFactory);
            // Initialize message source for this context.
            initMessageSource();
            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();
            // 模板方法,在容器刷新的時候能夠自定義邏輯,不一樣的Spring容器作不一樣的事情。
            onRefresh();
            // 註冊監聽器,廣播early application events
            registerListeners();
            // 實例化全部剩餘的(非懶加載)單例
            // 好比invokeBeanFactoryPostProcessors方法中根據各類註解解析出來的類,在這個時候都會被初始化。
            // 實例化的過程各類BeanPostProcessor開始起做用。
            finishBeanFactoryInitialization(beanFactory);
            // refresh作完以後須要作的其餘事情。
            // 清除上下文資源緩存(如掃描中的ASM元數據)
            // 初始化上下文的生命週期處理器,並刷新(找出Spring容器中實現了Lifecycle接口的bean並執行start()方法)。
            // 發佈ContextRefreshedEvent事件告知對應的ApplicationListener進行響應的操做
            finishRefresh();
        } catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                        "cancelling refresh attempt: " + ex);
            }
            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();
            // Reset 'active' flag.
            cancelRefresh(ex);
            // Propagate exception to caller.
            throw ex;
        } finally {
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
        }
    }
}
咱們只要注意這個方法 finishBeanFactoryInitialization() spring全部非內置的單例 javabean都是在這裏初始化的 , 主要是下面的這一行
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    ...

    // Instantiate all remaining (non-lazy-init) singletons.
    beanFactory.preInstantiateSingletons();
}

這行作了beanFactory中全部單例 javabean初始化和依賴處理的, 進入看一下post

@Override
public void preInstantiateSingletons() throws BeansException {
    ....
    // Trigger initialization of all non-lazy singleton beans...
    for (String beanName : beanNames) {
        RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
        if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
            if (isFactoryBean(beanName)) {
                Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
                if (bean instanceof FactoryBean) {
                    final FactoryBean<?> factory = (FactoryBean<?>) bean;
                    boolean isEagerInit;
                    if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                        isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
                                        ((SmartFactoryBean<?>) factory)::isEagerInit,
                                getAccessControlContext());
                    }
                    else {
                        isEagerInit = (factory instanceof SmartFactoryBean &&
                                ((SmartFactoryBean<?>) factory).isEagerInit());
                    }
                    if (isEagerInit) {
                        getBean(beanName);
                    }
                }
            }
            else {
                getBean(beanName);
            }
        }
    }
    ......
}
其實他的核心邏輯就是在這裏,經過使用getBean方法來盡心初始化的-> 而這個方法一樣也是咱們每次使用ApplicationContext獲取對象的時候所須要調用的類

這個getBean方法很是長,其實本質上就是嘗試獲取一個javabean,內部實現了bean的建立,依賴管理等等的邏輯,貼一個單例模式的核心代碼吧

@SuppressWarnings("unchecked")
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
        @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
    ....
    // Create bean instance.
    if (mbd.isSingleton()) {
        sharedInstance = getSingleton(beanName, () -> {
            try {
                return createBean(beanName, mbd, args);
            }
            catch (BeansException ex) {
                // Explicitly remove instance from singleton cache: It might have been put there
                // eagerly by the creation process, to allow for circular reference resolution.
                // Also remove any beans that received a temporary reference to the bean.
                destroySingleton(beanName);
                throw ex;
            }
        });
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }
    ....
}

外層的getSingleton 方法天然就是獲取單例而其中的會掉就是當單例不存在的時候,進行建立的邏輯,進入一下這個createBean

這裏代碼依然不少可是這個類最終依賴得了doCreateBean方法,因此直接看doCreateBean方法裏有啥,可是這裏執行了一個前置處理邏輯(postProcessBeforeInstantiation)方法來超前處理bean初始化
try {
    // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
    Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
    if (bean != null) {
        return bean;
    }
}
catch (Throwable ex) {
    throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
            "BeanPostProcessor before instantiation of bean failed", ex);
}
try {
    Object beanInstance = doCreateBean(beanName, mbdToUse, args);
    if (logger.isTraceEnabled()) {
        logger.trace("Finished creating instance of bean '" + beanName + "'");
    }
    return beanInstance;
}
接下來就是重點了doCreateBean方法,解決了循環引用問題和實現提前曝光的邏輯都是在這裏
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
        throws BeanCreationException {
            ......
    // Eagerly cache singletons to be able to resolve circular references
    // even when triggered by lifecycle interfaces like BeanFactoryAware.
    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
            isSingletonCurrentlyInCreation(beanName));
    if (earlySingletonExposure) {
        if (logger.isTraceEnabled()) {
            logger.trace("Eagerly caching bean '" + beanName +
                    "' to allow for resolving potential circular references");
        }
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }

    // Initialize the bean instance.
    Object exposedObject = bean;
    try {
        populateBean(beanName, mbd, instanceWrapper);
        exposedObject = initializeBean(beanName, exposedObject, mbd);
    }
    catch (Throwable ex) {
        if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
            throw (BeanCreationException) ex;
        }
        else {
            throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
        }
    }

    if (earlySingletonExposure) {
        Object earlySingletonReference = getSingleton(beanName, false);
        if (earlySingletonReference != null) {
            if (exposedObject == bean) {
                exposedObject = earlySingletonReference;
            }
            else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                String[] dependentBeans = getDependentBeans(beanName);
                Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
                for (String dependentBean : dependentBeans) {
                    if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                        actualDependentBeans.add(dependentBean);
                    }
                }
                if (!actualDependentBeans.isEmpty()) {
                    throw new BeanCurrentlyInCreationException(beanName,
                            "Bean with name '" + beanName + "' has been injected into other beans [" +
                            StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                            "] in its raw version as part of a circular reference, but has eventually been " +
                            "wrapped. This means that said other beans do not use the final version of the " +
                            "bean. This is often the result of over-eager type matching - consider using " +
                            "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
                }
            }
        }
    }
    // Register bean as disposable.
    try {
        registerDisposableBeanIfNecessary(beanName, bean, mbd);
    }
    catch (BeanDefinitionValidationException ex) {
        throw new BeanCreationException(
                mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
    }

    return exposedObject;
}
這裏有幾個重點
  1. 咱們的javabean屬性注入是在 populateBean方法中實現的,他的內部邏輯是使用了遞歸來循環的填衝屬性. 這裏也處理了構造函數等等依賴關係.

這裏要插一句AutoWriterAnnotionBeanPostProcessor

AutoWriterAnnotionBeanPostProcessor , 在這裏有一個特殊的處理邏輯能夠借鑑一下

private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
    // Fall back to class name as cache key, for backwards compatibility with custom callers.
    String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
    // Quick check on the concurrent map first, with minimal locking.
    InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
    if (InjectionMetadata.needsRefresh(metadata, clazz)) {
        synchronized (this.injectionMetadataCache) {
            metadata = this.injectionMetadataCache.get(cacheKey);
            if (InjectionMetadata.needsRefresh(metadata, clazz)) {
                if (metadata != null) {
                    metadata.clear(pvs);
                }
                metadata = buildAutowiringMetadata(clazz);
                this.injectionMetadataCache.put(cacheKey, metadata);
            }
        }
    }
    return metadata;
}

AutoWriterAnnotionBeanPostProcessor 將須要的屬性,使用反射並將反射緩存了一下(injectionMetadataCache) , 來加快性能

  1. 依賴關係處理

其實看上面doCreate的邏輯有一個earlySingletonExposure變量, spring在執行到這裏的時候將會自動的將這個尚未進行初始化的類(也就是當bean爲單例 && 容器配置容許循環依賴 && bean正在建立)的時候,將會在BeanFactory中設置第三級緩存, key是當前的bean的名稱,value是一個工廠方法 , 很簡單就是返回當前的類

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}
ps: spring 如何解決循環引用的?

這個能夠看一下spring的getBean的邏輯 , 其中有一段這樣的代碼

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

若是第一層緩存沒有找到,就從早起類裏找若是早期類裏尚未,就使用工廠方法獲取

聊一下這三層緩存都幹了啥

第一層: 全部的初始化完成的單例bean
早期的bean: 被循環引用觸發了,而且尚未初始化完成的bean
第三層: 正在被初始化的bean

如何解決循環引用就很簡單了 , 若是A依賴了B 而且B依賴了A , 當A初始化了B的時候, B初始化時將會從第三層引用中獲取當前的提前曝光的類,從而實現瞭解決循環引用,而且spring的實現,還實現了多線程模式下也能夠進行循環應用的邏輯

還沒完,整個生命週期尚未完全搞懂

相關文章
相關標籤/搜索