最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring中Bean的加載與SpringBoot的初始化流程詳解

 更新時(shí)間:2021年11月17日 14:37:24   作者:逆襲的小學(xué)生  
這篇文章主要介紹了Spring中Bean的加載與SpringBoot的初始化流程詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

前言

一直對(duì)它們之間的關(guān)系感到好奇,SpringBoot既然是Spring的封裝,那么SpringBoot在初始化時(shí)應(yīng)該也會(huì)有Bean的加載,那么是在何時(shí)進(jìn)行加載的呢?

第一章 Spring中Bean的一些簡(jiǎn)單概念

1.1 SpingIOC簡(jiǎn)介

Spring啟動(dòng)時(shí)去讀取應(yīng)用程序提供的Bean配置信息,并在Spring容器中生成相應(yīng)的Bean定義注冊(cè)表,然后根據(jù)注冊(cè)表去實(shí)例化Bean,裝配好Bean之間的依賴關(guān)系,為上層提供準(zhǔn)備就緒的運(yùn)行環(huán)境.

Spring提供一個(gè)配置文件描述Bean與Bean之間的依賴關(guān)系,利用Java語(yǔ)言的反射功能實(shí)例化Bean,并建立Bean之間的依賴關(guān)系.

1.2 BeanFactory

BeanFactory是接口,提供了IOC容器最基本的形式,給具體的IOC容器的實(shí)現(xiàn)提供了規(guī)范。

1.2.1 BeanDefinition

主要用來描述Bean的定義,Spring在啟動(dòng)時(shí)會(huì)將Xml或者注解里Bean的定義解析成Spring內(nèi)部的BeanDefinition.

beanClass保存bean的class屬性,scop保存bean是否單例,abstractFlag保存該bean是否抽象,lazyInit保存是否延遲初始化,autowireMode保存是否自動(dòng)裝配,等等等

public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
        implements BeanDefinition, Cloneable {
    private volatile Object beanClass;
    private String scope = SCOPE_DEFAULT;
    private boolean abstractFlag = false;
    private boolean lazyInit = false;
    private int autowireMode = AUTOWIRE_NO;
    private int dependencyCheck = DEPENDENCY_CHECK_NONE;
    private String[] dependsOn;
    private ConstructorArgumentValues constructorArgumentValues;
    private MutablePropertyValues propertyValues;
    private String factoryBeanName;
    private String factoryMethodName;
    private String initMethodName;
    private String destroyMethodName;
}

1.2.2 BeanDefinitionRegistry

registerBeanDefinition方法主要是將BeanDefinition注冊(cè)到BeanFactory接口的實(shí)現(xiàn)類DefaultListableBeanFacory中的beanDefinitionMap中。

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

1.2.3 BeanFactory結(jié)構(gòu)圖

ListableBeanFactory該接口定義了訪問容器中Bean的若干方法,如查看Bean的個(gè)數(shù),獲取某一類型Bean的配置名,查看容器中是否包括某一Bean等方法.

HierarchicalBeanFactory是父子級(jí)聯(lián)的IOC容器接口,子容器可以通過接口方法訪問父容器,通過HierarchicalBeanFactory接口SpringIOC可以建立父子層級(jí)關(guān)聯(lián)的IOC層級(jí)體系,子容器可以訪問父容器的Bean,父容器不能訪問子容器的Bean,比如展現(xiàn)層的Bean位于子容器中而業(yè)務(wù)層和持久層的Bean位于父容器的Bean.

  • ConfigurableBeanFactory:增強(qiáng)了IOC接口的可定制性,定義了設(shè)置類裝載器,屬性遍歷器,以及屬性初始化后置處理器等方法.
  • AutowireCapableBeanFactory:定義了將容器中的Bean按某種規(guī)則,按名字匹配,按類型匹配等.
  • SingletonBeanRegistry:允許在運(yùn)行期間向容器注冊(cè)SingletonBean實(shí)例的方法.

通過這些接口也證明了BeanFactory的體系也確實(shí)提供了IOC的基礎(chǔ)及依賴注入和Bean的裝載等功能.

1.3 ApplicationContext

由于BeanFactory的功能還不夠強(qiáng)大,于是Spring在BeanFactory的基礎(chǔ)上還設(shè)計(jì)了一個(gè)更為高級(jí)的接口即ApplicationContext,它是BeanFactory的子接口之一.在我們使用SpringIOC容器時(shí),大部分都是context的實(shí)現(xiàn)類。

我理解著就是BeanFactory只提供IOC,ApplicationContext還提供很多別的功能。

第二章 SpringBoot的初始化流程

@SpringBootApplication
public class RepApplication {
	public static void main(String[] args) {
        //要理解的SpringApplication
	SpringApplication.run(RepApplication.class, args);
	}
}

SpringApplication的run分為兩個(gè)階段,即new SpringApplication()時(shí)的執(zhí)行構(gòu)造函數(shù)的準(zhǔn)備階段,和run時(shí)的運(yùn)行階段。

public static ConfigurableApplicationContext run(Class<?>[] primarySources,
			String[] args) {
		return new SpringApplication(primarySources).run(args);
	}

2.1 準(zhǔn)備階段

在準(zhǔn)備階段會(huì)

配置SpringBean的來源

推斷web應(yīng)用類型

加載應(yīng)用上下文初始器

加載應(yīng)用事件監(jiān)聽器

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        //推斷web應(yīng)用類型
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
        //加載應(yīng)用上下文初始化器
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
        //加載應(yīng)用事件監(jiān)聽器
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

2.2 運(yùn)行階段

  • 加載:SpringApplication獲得監(jiān)聽器
  • 運(yùn)行:SpringApplication運(yùn)行監(jiān)聽器
  • 監(jiān)聽:SpringBoot事件、Spring事件
  • 創(chuàng)建:應(yīng)用上下文、Enviroment、其它(不重要),應(yīng)用上下文創(chuàng)建后會(huì)被應(yīng)用上下文初始化器初始化,Enviroment是抽象的環(huán)境對(duì)象。
  • 失?。汗收戏治鰣?bào)告。
  • 回調(diào):CommandLineRunner、ApplicationRunner
public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
        //獲得監(jiān)聽器
		SpringApplicationRunListeners listeners = getRunListeners(args);
        //運(yùn)行監(jiān)聽器
		listeners.starting();
		try {
            //應(yīng)用上下文
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
            //環(huán)境
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
            //依據(jù)不同的配置加載不同的ApplicationContext
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			listeners.started(context);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}
 
		try {
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

2.2.1 監(jiān)聽器分析

這個(gè)是看了源碼后的個(gè)人理解,不保證一定正確,只提供一定的參考。

在準(zhǔn)備階段加載實(shí)現(xiàn)了ApplicationListener的監(jiān)聽器。

然后在運(yùn)行階段調(diào)用了starting()方法。

//獲得監(jiān)聽器
SpringApplicationRunListeners listeners = getRunListeners(args);
//運(yùn)行監(jiān)聽器
listeners.starting();

下面是listeners的源碼,可以看到它的每一個(gè)方法,都對(duì)應(yīng)SpringBoot的一個(gè)階段,這表明每到對(duì)應(yīng)的階段,都要廣播對(duì)應(yīng)的事件。

class SpringApplicationRunListeners { 
	private final Log log; 
	private final List<SpringApplicationRunListener> listeners; 
	SpringApplicationRunListeners(Log log,
			Collection<? extends SpringApplicationRunListener> listeners) {
		this.log = log;
		this.listeners = new ArrayList<>(listeners);
	}
 
	public void starting() {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.starting();
		}
	}
 
	public void environmentPrepared(ConfigurableEnvironment environment) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.environmentPrepared(environment);
		}
	}
 
	public void contextPrepared(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.contextPrepared(context);
		}
	}
 
	public void contextLoaded(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.contextLoaded(context);
		}
	}
 
	public void started(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.started(context);
		}
	} 
	//省略... 
}

那么問題來了,廣播事件后,事件是怎么被監(jiān)聽到的呢?我們打開listener.environmentPrepared(environment)的源碼,發(fā)現(xiàn)其調(diào)用了initialMulticaster進(jìn)行了事件廣播

@Override
	public void environmentPrepared(ConfigurableEnvironment environment) {
		this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
				this.application, this.args, environment));
	}

廣播的代碼如下,看了一下感覺大意就是找到根事件匹配的監(jiān)聽器,然后調(diào)用線程池去執(zhí)行對(duì)應(yīng)的觸發(fā)函數(shù)。

@Override
	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			Executor executor = getTaskExecutor();
			if (executor != null) {
				executor.execute(() -> invokeListener(listener, event));
			}
			else {
				invokeListener(listener, event);
			}
		}
	}

2.2.2 refreshContext

再往下最核心的是refreshContext方法,一直點(diǎn)進(jìn)去可以看到如下:

  • prepareRefresh:完成配置之類的解析,設(shè)置Spring的狀態(tài),初始化屬性源信息,驗(yàn)證環(huán)境信息中必須存在的屬性.
  • ConfigurableListableBeanFactory:是用來獲取beanFactory的實(shí)例的(第一張也寫過BeanFactory負(fù)責(zé)bean的加載與獲?。?。
  • PrepareBeanFactory:對(duì)beanFactory進(jìn)行相關(guān)的設(shè)置,為后續(xù)的使用做準(zhǔn)備,包括設(shè)置classLoader用來加載Bean,設(shè)置表達(dá)式解析器等等.
  • postProcessBeanFactory:是用于在BeanFactory設(shè)置之后進(jìn)行后續(xù)的BeanFactory的操作.
  • invokeBeanFactoryPostProcessors:點(diǎn)進(jìn)去發(fā)現(xiàn)調(diào)用了如下方法,點(diǎn)進(jìn)去之后發(fā)現(xiàn)邏輯相當(dāng)復(fù)雜,主要調(diào)用工廠后處理器,調(diào)用Bean標(biāo)簽,掃描Bean文件,并解析成一個(gè)個(gè)的Bean,這時(shí)候這些Bean是被加載進(jìn)了Spirng容器當(dāng)中,這里涉及了各種類,我們?cè)谶@里主要說一下ConfigurationClassParser,主要是解析Bean的類.該方法會(huì)對(duì)帶有@configuration,@import,@bean,以及@SpringBootApplication等標(biāo)簽的Bean進(jìn)行解析,
  • registerBeanPostProcessors:會(huì)從Spring容器中找出實(shí)現(xiàn)BeanPostProcessors接口的Bean,并設(shè)置到BeanFactory的屬性之中,之后Bean實(shí)例化時(shí)會(huì)調(diào)用BeanProcessor,也就是Bean的后置處理器.會(huì)和AOP比較相關(guān).
  • initMessageSource:初始化消息源(這個(gè)自己推斷的)
  • initApplicationEventMuticaster:初始化事件廣播器
  • onRefresh:是一個(gè)模板方法,不同的Spring容器會(huì)重寫它做不同的事情.比如web程序的容器,會(huì)調(diào)用create..方法去創(chuàng)建內(nèi)置的servlet容器.
  • registerListeners:注冊(cè)事件監(jiān)聽器
  • finishBeanFactoryInitialization:會(huì)實(shí)例化BeanFactory中已被注冊(cè)但未被實(shí)例化的所有實(shí)例,懶加載是不需要被實(shí)例化的.前面的invokeBeanFactoryPostProcessors方法中根據(jù)各種注解解析出來的Bean在這個(gè)時(shí)候都會(huì)被初始化,同時(shí)初始化過程中的各種PostProcessor就會(huì)開始起作用了.
  • finishRefresh:會(huì)做初始化生命周期處理器相關(guān)的事情.
@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();
 
			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
 
			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);
 
			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);
 
				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);
 
				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);
 
				// Initialize message source for this context.
				initMessageSource();
 
				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();
 
				// Initialize other special beans in specific context subclasses.
				onRefresh();
 
				// Check for listener beans and register them.
				registerListeners();
 
				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);
 
				// Last step: publish corresponding event.
				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();
			}
		}
	}

當(dāng)上面的代碼執(zhí)行完畢后,返回上層代碼,后面是注冊(cè)鉤子,這鉤子是希望開發(fā)者能結(jié)合自己的實(shí)際需求擴(kuò)展出一些在Spring容器關(guān)閉時(shí)的行為.

private void refreshContext(ConfigurableApplicationContext context) {
		refresh(context);
		if (this.registerShutdownHook) {
			try {
				context.registerShutdownHook();
			}
			catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
	}

繼續(xù)返回上層代碼,可以看到afterRefresh方法它的方法體是空的, 也就說明Spring框架考慮了擴(kuò)展性,留了很多的口子,讓大家在框架層面繼承很多的模塊并去做自定義的實(shí)現(xiàn)

protected void afterRefresh(ConfigurableApplicationContext context,
			ApplicationArguments args) {
	}

2.3 總結(jié)

總的來說,SpringBoot加載的Bean的時(shí)機(jī)為,點(diǎn)進(jìn)一開始的run方法,層層遞進(jìn)后,由

refreshContext(context);

進(jìn)行了bean的加載,更詳細(xì)的話,那就層層遞進(jìn)點(diǎn)進(jìn)去,是在如下方法進(jìn)行了bean的加載。

invokeBeanFactoryPostProcessors(beanFactory);

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java中終止線程的方法詳解

    Java中終止線程的方法詳解

    這篇文章主要介紹了Java中終止線程的方法詳解的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • 淺談java switch如果case后面沒有break,會(huì)出現(xiàn)什么情況?

    淺談java switch如果case后面沒有break,會(huì)出現(xiàn)什么情況?

    這篇文章主要介紹了淺談java switch如果case后面沒有break,會(huì)出現(xiàn)什么情況?具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨想小編過來看看吧
    2020-09-09
  • java插入排序 Insert sort實(shí)例

    java插入排序 Insert sort實(shí)例

    java插入排序 Insert sort實(shí)例代碼,需要的朋友可以參考一下
    2013-03-03
  • Java設(shè)計(jì)模式之策略模式的使用(Strategy?Pattern)

    Java設(shè)計(jì)模式之策略模式的使用(Strategy?Pattern)

    策略模式是一種行為型設(shè)計(jì)模式,用于定義一系列算法并將每個(gè)算法封裝起來,使它們可以互相替換,從而實(shí)現(xiàn)代碼的可維護(hù)性和靈活性,策略模式包含策略接口、具體策略類和上下文類,并通過將算法的選擇與使用分離,使得算法可以獨(dú)立變化
    2025-03-03
  • Java中接口和抽象類的區(qū)別與相同之處

    Java中接口和抽象類的區(qū)別與相同之處

    這篇文章主要介紹了Java中接口和抽象類的區(qū)別與相同之處,本文講解了抽象類的概念、接口的概念、接口和抽象類的區(qū)別與聯(lián)系等內(nèi)容,需要的朋友可以參考下
    2015-06-06
  • 解決Java Calendar類set()方法的陷阱

    解決Java Calendar類set()方法的陷阱

    這篇文章主要介紹了解決Java Calendar類set()方法的陷阱,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • 淺談Mybatis #和$區(qū)別以及原理

    淺談Mybatis #和$區(qū)別以及原理

    這篇文章主要介紹了淺談Mybatis #和$區(qū)別以及原理,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • 一文總結(jié)Java獲取文件后綴名的所有方法

    一文總結(jié)Java獲取文件后綴名的所有方法

    Java是一種應(yīng)用廣泛的編程語(yǔ)言,可以通過多種方式來實(shí)現(xiàn)對(duì)文件的操作,如文件名后綴、文件擴(kuò)展名等,下面這篇文章主要給大家介紹了關(guān)于Java獲取文件后綴名的所有方法,需要的朋友可以參考下
    2023-05-05
  • 詳解如何使用MyBatis實(shí)現(xiàn)數(shù)據(jù)庫(kù)的CRUD

    詳解如何使用MyBatis實(shí)現(xiàn)數(shù)據(jù)庫(kù)的CRUD

    這篇文章主要為大家詳細(xì)介紹了如何使用MyBatis實(shí)現(xiàn)數(shù)據(jù)庫(kù)的CRUD操作,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-12-12
  • 解決JavaEE開發(fā)中字符編碼出現(xiàn)亂碼的問題

    解決JavaEE開發(fā)中字符編碼出現(xiàn)亂碼的問題

    下面小編就為大家?guī)硪黄鉀QJavaEE開發(fā)中字符編碼出現(xiàn)亂碼的問題。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-07-07

最新評(píng)論

隆德县| 额尔古纳市| 迭部县| 贵州省| 图片| 家居| 新竹市| 治县。| 高要市| 宁都县| 武鸣县| 龙岩市| 华安县| 浦东新区| 右玉县| 共和县| 五家渠市| 名山县| 兴化市| 泽普县| 南江县| 太仓市| 盐亭县| 泽州县| 沈丘县| 隆尧县| 安丘市| 乌鲁木齐市| 田东县| 闽侯县| 涿鹿县| 涪陵区| 玛纳斯县| 保靖县| 乳源| 曲阜市| 彭水| 大石桥市| 山东省| 贡嘎县| 久治县|