AVt天堂网 手机版,亚洲va久久久噜噜噜久久4399,天天综合亚洲色在线精品,亚洲一级Av无码毛片久久精品

當(dāng)前位置:首頁 > 科技  > 軟件

玩轉(zhuǎn)SpringBoot—啟動(dòng)源碼及外部化配置

來源: 責(zé)編: 時(shí)間:2023-10-08 07:05:34 251觀看
導(dǎo)讀學(xué)習(xí)目標(biāo)理解springboot的總體啟動(dòng)流程,并能口述大概理清配置文件的加載流程第1章 main入口public static void main(String[] args) { //代碼很簡單SpringApplication.run(); SpringApplication.run(ConsumerApp.c

E2s28資訊網(wǎng)——每日最新資訊28at.com

學(xué)習(xí)目標(biāo)

  • 理解springboot的總體啟動(dòng)流程,并能口述大概
  • 理清配置文件的加載流程

第1章 main入口

E2s28資訊網(wǎng)——每日最新資訊28at.com

public static void main(String[] args) {    //代碼很簡單SpringApplication.run();	SpringApplication.run(ConsumerApp.class, args);}
public static ConfigurableApplicationContext run(Class<?> primarySource,                                                 String... args) {    //這個(gè)里面調(diào)用了run() 方法,我們轉(zhuǎn)到定義    return run(new Class<?>[] { primarySource }, args);}//這個(gè)run方法代碼也很簡單,就做了兩件事情//1、new了一個(gè)SpringApplication() 這么一個(gè)對象//2、執(zhí)行new出來的SpringApplication()對象的run()方法public static ConfigurableApplicationContext run(Class<?>[] primarySources,                                                 String[] args) {    return new SpringApplication(primarySources).run(args);}

上面代碼主要做了兩件事情。E2s28資訊網(wǎng)——每日最新資訊28at.com

  • 第一步new了一個(gè)SpringApplication對象
  • 第二步調(diào)用了run()方法。

一、SpringApplication

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {		this.resourceLoader = resourceLoader;		Assert.notNull(primarySources, "PrimarySources must not be null");		//1、先把主類保存起來		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));		//2、判斷運(yùn)行項(xiàng)目的類型		this.webApplicationType = WebApplicationType.deduceFromClasspath();		//3、掃描當(dāng)前路徑下META-INF/spring.factories文件的,加載ApplicationContextInitializer接口實(shí)例		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));		//4、掃描當(dāng)前路徑下META-INF/spring.factories文件的,加載ApplicationListener接口實(shí)例		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));		this.mainApplicationClass = deduceMainApplicationClass();}

1、判斷運(yùn)行環(huán)境

構(gòu)造方法內(nèi)會(huì)調(diào)用枚舉WebApplicationType的deduceFromClasspath方法獲得應(yīng)用類型并設(shè)置當(dāng)前應(yīng)用是普通web應(yīng)用、響應(yīng)式web應(yīng)用還是非web應(yīng)用。E2s28資訊網(wǎng)——每日最新資訊28at.com

this.webApplicationType = WebApplicationType.deduceFromClasspath();

deduceFromClasspath方法由枚舉WebApplicationType提供,具體實(shí)現(xiàn)如下:E2s28資訊網(wǎng)——每日最新資訊28at.com

static WebApplicationType deduceFromClasspath() {    //當(dāng)classpath下只存在org.springframework.web.reactive.DispatcherHandler,    //且不存在org.springframework.web.servlet.DispatcherServlet,也不存在    //org.glassfish.jersey.servlet.ServletContainer則運(yùn)行環(huán)境為reactive,該模式是非阻塞模式    if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)        && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {        return WebApplicationType.REACTIVE;    }    for (String className : SERVLET_INDICATOR_CLASSES) {        if (!ClassUtils.isPresent(className, null)) {            return WebApplicationType.NONE;        }    }    return WebApplicationType.SERVLET;}

推斷的過程中重點(diǎn)調(diào)用了ClassUtils.isPresent()方法,用來判斷指定類名的類是否存在,是否可以進(jìn)行加載。ClassUtils.isPresent()方法源代碼如下:E2s28資訊網(wǎng)——每日最新資訊28at.com

public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {    try {        forName(className, classLoader);        return true;    }    catch (IllegalAccessError err) {        throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" +                                        className + "]: " + err.getMessage(), err);    }    catch (Throwable ex) {        // Typically ClassNotFoundException or NoClassDefFoundError...        return false;    }}

isPresent()方法調(diào)用了forName()方法,如果在調(diào)用forName()方法的過程中出現(xiàn)異常則返回false,也就是目標(biāo)類不存在。否則,返回true。E2s28資訊網(wǎng)——每日最新資訊28at.com

看一下forName()方法的部分代碼:E2s28資訊網(wǎng)——每日最新資訊28at.com

public static Class<?> forName(String name, @Nullable ClassLoader classLoader)			throws ClassNotFoundException, LinkageError {	// 此處省略一些非空和基礎(chǔ)類型的判斷邏輯代碼	ClassLoader clToUse = classLoader;	if (clToUse == null) {	    //如果為空則獲取默認(rèn)classLoader		clToUse = getDefaultClassLoader();	}	try {	    // 返回加載戶的Class。		return Class.forName(name, false, clToUse);	} catch (ClassNotFoundException ex) {	    // 如果直接加載類出現(xiàn)異常,則嘗試加載內(nèi)部類。		int lastDotIndex = name.lastIndexOf(PACKAGE_SEPARATOR);		if (lastDotIndex != -1) {		    // 拼接內(nèi)部類			String innerClassName =					name.substring(0, lastDotIndex) + INNER_CLASS_SEPARATOR + name.substring(lastDotIndex + 1);			try {				return Class.forName(innerClassName, false, clToUse);			}			catch (ClassNotFoundException ex2) {				// Swallow - let original exception get through			}		}		throw ex;	}}

通過以上核心代碼,可得知forName()方法主要做的事情就是獲得類加載器,嘗試直接加載類,如果失敗則嘗試加載該類的內(nèi)部類,如果依舊失敗,則拋出異常。E2s28資訊網(wǎng)——每日最新資訊28at.com

因此,整個(gè)應(yīng)用類型的推斷分以下步驟:E2s28資訊網(wǎng)——每日最新資訊28at.com

  • SpringBoot調(diào)用SpringApplication構(gòu)造方法;
  • SpringApplication構(gòu)造方法調(diào)用枚舉類的類型推斷方法deduceFromClasspath()。
  • deduceFromClasspath()方法通過ClassUtils.isPresent()返回結(jié)果為true或false來確定是否加載成功指定的類。
  • ClassUtils.isPresent()方法通過調(diào)用forName()方法并捕獲異常來確定是否能夠成功加載該類。
  • forName()方法通過嘗試加載指定類和指定類的內(nèi)部類來確定該類是否存在,存在則返回該類,不存在則拋異常。

在類型推斷的過程中枚舉類WebApplicationType定義了具體去加載哪些類:E2s28資訊網(wǎng)——每日最新資訊28at.com

private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",			"org.springframework.web.context.ConfigurableWebApplicationContext" };private static final String WEBMVC_INDICATOR_CLASS = "org.springframework."    + "web.servlet.DispatcherServlet";private static final String WEBFLUX_INDICATOR_CLASS = "org."    + "springframework.web.reactive.DispatcherHandler";private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
  • 如果應(yīng)用程序存在DispatcherHandler并且不存在DispatcherServlet和ServletContainer則為響應(yīng)式web應(yīng)用,需加載并啟動(dòng)內(nèi)嵌的響應(yīng)式web服務(wù)。
  • 如果應(yīng)用程序不包含Servlet和ConfigurableWebApplicationContext則為普通應(yīng)用程序。
  • 其他情況則為基于servlet的web應(yīng)用,需加載并啟動(dòng)內(nèi)嵌的web服務(wù)。

2、初始化器和監(jiān)聽器

利用SPI機(jī)制掃描 META-INF/spring.factories 這個(gè)文件,并且加載ApplicationContextInitializer、ApplicationListener 接口實(shí)例。E2s28資訊網(wǎng)——每日最新資訊28at.com

  • ApplicationContextInitializer 這個(gè)類當(dāng)springboot上下文Context初始化完成后會(huì)調(diào)用。
  • ApplicationListener 當(dāng)springboot啟動(dòng)時(shí)事件change后都會(huì)觸發(fā)。

總結(jié):上面就是SpringApplication初始化的代碼,new SpringApplication()沒做啥事情 ,利用SPI機(jī)制主要加載了META-INF/spring.factories 下面定義的事件監(jiān)聽器接口實(shí)現(xiàn)類。E2s28資訊網(wǎng)——每日最新資訊28at.com

二、執(zhí)行run方法

public ConfigurableApplicationContext run(String... args) {    <!--1、這個(gè)是一個(gè)計(jì)時(shí)器,沒什么好說的-->    StopWatch stopWatch = new StopWatch();    stopWatch.start();    DefaultBootstrapContext bootstrapContext = createBootstrapContext();    ConfigurableApplicationContext context = null;        <!--2、這個(gè)也不是重點(diǎn),就是設(shè)置了一些環(huán)境變量-->    configureHeadlessProperty();     <!--3、獲取事件監(jiān)聽器SpringApplicationRunListener類型,并且執(zhí)行starting()方法-->    SpringApplicationRunListeners listeners = getRunListeners(args);    listeners.starting(bootstrapContext, this.mainApplicationClass);    try {        <!--4、把參數(shù)args封裝成DefaultApplicationArguments,這個(gè)了解一下就知道-->        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);        <!--5、這個(gè)很重要準(zhǔn)備環(huán)境了,并且把環(huán)境跟spring上下文綁定好,并且執(zhí)行environmentPrepared()方法-->            //準(zhǔn)備容器環(huán)境、這里會(huì)加載配置文件。在這個(gè)方法里面會(huì)調(diào)用所有監(jiān)聽器Listener的onApplicationEvent(event);            // 此時(shí)有一個(gè)與配置文件相關(guān)的監(jiān)聽器就會(huì)被加載`ConfigFileApplicationListener`        ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);        <!--6、判斷一些環(huán)境的值,并設(shè)置一些環(huán)境的值-->        configureIgnoreBeanInfo(environment);        <!--7、打印banner-->        Banner printedBanner = printBanner(environment);        <!--8、創(chuàng)建上下文,根據(jù)項(xiàng)目類型創(chuàng)建上下文-->        context = createApplicationContext();		context.setApplicationStartup(this.applicationStartup);                <!--9、準(zhǔn)備上下文,執(zhí)行完成后調(diào)用contextPrepared()方法,contextLoaded()方法-->        prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);        <!--10、這個(gè)是spring啟動(dòng)的代碼了,這里就回去里面就回去掃描并且初始化單實(shí)列bean了-->        //這個(gè)refreshContext()加載了bean,還啟動(dòng)了內(nèi)置web容器,需要細(xì)細(xì)的去看看        refreshContext(context);        <!--11、啥事情都沒有做-->        afterRefresh(context, applicationArguments);        stopWatch.stop();        if (this.logStartupInfo) {            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);        }        <!--12、執(zhí)行ApplicationRunListeners中的started()方法-->        listeners.started(context);        <!--執(zhí)行Runner(ApplicationRunner和CommandLineRunner)-->        callRunners(context, applicationArguments);    }    catch (Throwable ex) {        handleRunFailure(context, ex, listeners);        throw new IllegalStateException(ex);    }    try {        listeners.running(context);    }    catch (Throwable ex) {        handleRunFailure(context, ex, null);        throw new IllegalStateException(ex);    }    return context;}

第2章 環(huán)境變量及配置

一、prepareEnvironment

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,                  DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {    // 創(chuàng)建和配置環(huán)境變量    ConfigurableEnvironment environment = getOrCreateEnvironment();    configureEnvironment(environment, applicationArguments.getSourceArgs());    ConfigurationPropertySources.attach(environment);    listeners.environmentPrepared(bootstrapContext, environment);    DefaultPropertiesPropertySource.moveToEnd(environment);    Assert.state(!environment.containsProperty("spring.main.environment-prefix"),                 "Environment prefix cannot be set via properties.");    bindToSpringApplication(environment);    if (!this.isCustomEnvironment) {        environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,                                                                                               deduceEnvironmentClass());    }    ConfigurationPropertySources.attach(environment);    return environment;}

二、getOrCreateEnvironment

/***  該方法根據(jù)webApplicationType判斷當(dāng)前項(xiàng)目是什么類型項(xiàng)目*/private ConfigurableEnvironment getOrCreateEnvironment() {    if (this.environment != null) {        return this.environment;    }    switch (this.webApplicationType) {        case SERVLET:            return new StandardServletEnvironment();        case REACTIVE:            return new StandardReactiveWebEnvironment();        default:            return new StandardEnvironment();    }}//webApplicationType是在new SpringApplication方法中通過WebApplicationType.deduceFromClasspath()進(jìn)行賦值

枚舉WebApplicationType中定義了三個(gè)應(yīng)用類型:E2s28資訊網(wǎng)——每日最新資訊28at.com

  • NONE:應(yīng)用程序不作為web應(yīng)用啟動(dòng),不啟動(dòng)內(nèi)嵌的服務(wù)。
  • SERVLET:應(yīng)用程序以基于servlet的web應(yīng)用啟動(dòng),需啟動(dòng)內(nèi)嵌servlet web服務(wù)。
  • REACTIVE:應(yīng)用程序以響應(yīng)式web應(yīng)用啟動(dòng),需啟動(dòng)內(nèi)嵌的響應(yīng)式web服務(wù)。

這里調(diào)用newStandardServletEnvironment()方法。E2s28資訊網(wǎng)——每日最新資訊28at.com

StandardServletEnvironment繼承了StandardEnvironment方法,StandardEnvironment又繼承了AbstractEnvironment方法;在AbstractEnvironment方法中調(diào)用了customizePropertySources方法。E2s28資訊網(wǎng)——每日最新資訊28at.com

public AbstractEnvironment() {    customizePropertySources(this.propertySources);}

customizePropertySources方法會(huì)回調(diào)StandardServletEnvironment方法中的customizePropertySources方法。E2s28資訊網(wǎng)——每日最新資訊28at.com

protected void customizePropertySources(MutablePropertySources propertySources) {    propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));    propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));    if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {        propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));    }    //在這里又調(diào)用了父類StandardEnvironment的方法    super.customizePropertySources(propertySources);}

到這里為止propertySources里面就加載了servletConfigInitParams、servletContextInitParams、systemProperties、systemEnvironment。E2s28資訊網(wǎng)——每日最新資訊28at.com

然后回到prepareEnvironment方法中,在listeners.environmentPrepared(bootstrapContext, environment);方法中去進(jìn)行監(jiān)聽。E2s28資訊網(wǎng)——每日最新資訊28at.com

三、environmentPrepared

void environmentPrepared(ConfigurableEnvironment environment) {    for (SpringApplicationRunListener listener : this.listeners) {        listener.environmentPrepared(environment);    }}

繼續(xù)進(jìn)入environmentPrepared方法,會(huì)進(jìn)入到SpringApplicationRunListener接口,這個(gè)接口在run方法中的getRunListeners里面獲取,最終是在sprin.factories里面進(jìn)行加載實(shí)現(xiàn)類EventPublishingRunListener,執(zhí)行的是EventPublishingRunListener類中的environmentPrepared方法。E2s28資訊網(wǎng)——每日最新資訊28at.com

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

multicastEvent

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

invokeListener

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {    ErrorHandler errorHandler = getErrorHandler();    if (errorHandler != null) {        try {            doInvokeListener(listener, event);        }        catch (Throwable err) {            errorHandler.handleError(err);        }    }    else {        doInvokeListener(listener, event);    }}

doInvokeListener

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {    try {        listener.onApplicationEvent(event);    }    catch (ClassCastException ex) {        String msg = ex.getMessage();        if (msg == null || matchesClassCastMessage(msg, event.getClass())) {            // Possibly a lambda-defined listener which we could not resolve the generic event type for            // -> let's suppress the exception and just log a debug message.            Log logger = LogFactory.getLog(getClass());            if (logger.isTraceEnabled()) {                logger.trace("Non-matching event type for listener: " + listener, ex);            }        }        else {            throw ex;        }    }}

進(jìn)入ConfigFileApplicationListener實(shí)現(xiàn)類中的onApplicationEvent方法。E2s28資訊網(wǎng)——每日最新資訊28at.com

onApplicationEvent

public void onApplicationEvent(ApplicationEvent event) {    if (event instanceof ApplicationEnvironmentPreparedEvent) {        //在這個(gè)方法里面讀取配置文件        onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);    }    if (event instanceof ApplicationPreparedEvent) {        onApplicationPreparedEvent(event);    }}
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {    List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();    postProcessors.add(this);    AnnotationAwareOrderComparator.sort(postProcessors);    for (EnvironmentPostProcessor postProcessor : postProcessors) {        //進(jìn)入        postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());    }}
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {    //進(jìn)入    addPropertySources(environment, application.getResourceLoader());}
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {    RandomValuePropertySource.addToEnvironment(environment);    //load方法是讀取配置文件的核心方法    new Loader(environment, resourceLoader).load();}
void load() {    FilteredPropertySource.apply(this.environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY,                                 (defaultProperties) -> {                                     this.profiles = new LinkedList<>();                                     this.processedProfiles = new LinkedList<>();                                     this.activatedProfiles = false;                                     this.loaded = new LinkedHashMap<>();                                     initializeProfiles();                                     while (!this.profiles.isEmpty()) {                                         Profile profile = this.profiles.poll();                                         if (isDefaultProfile(profile)) {                                             addProfileToEnvironment(profile.getName());                                         }                                         load(profile, this::getPositiveProfileFilter,                                              addToLoaded(MutablePropertySources::addLast, false));                                         this.processedProfiles.add(profile);                                     }                                     load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));                                     addLoadedPropertySources();                                     applyActiveProfiles(defaultProperties);                                 });}

createApplicationContext

一起來看下context = createApplicationContext(); 這段代碼,這段代碼主要是根據(jù)項(xiàng)目類型創(chuàng)建上下文,并且會(huì)注入幾個(gè)核心組件類。E2s28資訊網(wǎng)——每日最新資訊28at.com

protected ConfigurableApplicationContext createApplicationContext() {	Class<?> contextClass = this.applicationContextClass;	if (contextClass == null) {		try {			switch (this.webApplicationType) {			case SERVLET:				contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);				break;			case REACTIVE:				contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);				break;			default:				contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);			}		}		catch (ClassNotFoundException ex) {			throw new IllegalStateException(					"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);		}	}	return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);}
public AnnotationConfigServletWebServerApplicationContext(DefaultListableBeanFactory beanFactory) {	   super(beanFactory);       //1:會(huì)去注入一些spring核心組件	   this.reader = new AnnotatedBeanDefinitionReader(this);	   this.scanner = new ClassPathBeanDefinitionScanner(this);}

Web類型項(xiàng)目創(chuàng)建上下文對象AnnotationConfigServletWebServerApplicationContext 。這里會(huì)把 ConfigurationClassPostProcessor 、AutowiredAnnotationBeanPostProcessor 等一些核心組件加入到Spring容器。E2s28資訊網(wǎng)——每日最新資訊28at.com

五、refreshContext

下面一起來看下refreshContext(context) 這個(gè)方法,這個(gè)方法啟動(dòng)spring的代碼加載了bean,還啟動(dòng)了內(nèi)置web容器。E2s28資訊網(wǎng)——每日最新資訊28at.com

private void refreshContext(ConfigurableApplicationContext context) {        // 轉(zhuǎn)到定義看看		refresh(context);		if (this.registerShutdownHook) {			try {				context.registerShutdownHook();			}			catch (AccessControlException ex) {				// Not allowed in some environments.			}		}}
protected void refresh(ApplicationContext applicationContext) {		Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);        //看看refresh()方法去		((AbstractApplicationContext) applicationContext).refresh();}

轉(zhuǎn)到AbstractApplicationContext - >refresh()方法里面發(fā)現(xiàn)這是spring容器啟動(dòng)代碼。E2s28資訊網(wǎng)——每日最新資訊28at.com

/*** 加載或刷新一個(gè)持久化的配置,可能是XML文件、屬性文件或關(guān)系數(shù)據(jù)庫模式。* 由于這是一種啟動(dòng)方法,如果失敗,應(yīng)該銷毀已經(jīng)創(chuàng)建的單例,以避免懸空資源。* 換句話說,在調(diào)用該方法之后,要么全部實(shí)例化,要么完全不實(shí)例化。* @throws 如果bean工廠無法初始化,則拋出 BeansException 異常* @throws 如果已經(jīng)初始化且不支持多次刷新,則會(huì)拋出 IllegalStateException 異常*/@Overridepublic void refresh() throws BeansException, IllegalStateException {    //加載或刷新配置前的同步處理    synchronized (this.startupShutdownMonitor) {        // 為刷新而準(zhǔn)備此上下文        prepareRefresh();        // 告訴子類去刷新內(nèi)部bean工廠。        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();        // 準(zhǔn)備好bean工廠,以便在此上下文中使用。        prepareBeanFactory(beanFactory);        try {            // 允許在上下文子類中對bean工廠進(jìn)行后置處理。            postProcessBeanFactory(beanFactory);            // 調(diào)用在上下文中注冊為bean的工廠處理器。            invokeBeanFactoryPostProcessors(beanFactory);            // 注冊攔截bean創(chuàng)建的bean處理器。            registerBeanPostProcessors(beanFactory);            // 初始化此上下文的 message resource 消息資源。            initMessageSource();            // 為這個(gè)上下文初始化事件多路廣播器。            initApplicationEventMulticaster();            // 初始化特定上下文子類中的其他特殊bean。            onRefresh();            // 注冊監(jiān)聽器(檢查監(jiān)聽器的bean并注冊它們)。            registerListeners();            // 實(shí)例化所有剩余的(非 lazy-init 懶初始化的)單例。            finishBeanFactoryInitialization(beanFactory);            // 最后一步: 發(fā)布相應(yīng)的事件。            finishRefresh();        }        catch (BeansException ex) {            if (logger.isWarnEnabled()) {                logger.warn("Exception encountered during context initialization - " +                            "cancelling refresh attempt: " + ex);            }            // 銷毀已經(jīng)創(chuàng)建的單例,以避免懸空資源。            destroyBeans();            // 重置 'active' 表示.            cancelRefresh(ex);            // 將異常傳播給調(diào)用者。            throw ex;        }        finally {            // 重置Spring內(nèi)核中的共用的緩存,因?yàn)槲覀兛赡茉僖膊恍枰獑卫齜ean的元數(shù)據(jù)了……            resetCommonCaches();        }    }}

本文鏈接:http://www.tebozhan.com/showinfo-26-12316-0.html玩轉(zhuǎn)SpringBoot—啟動(dòng)源碼及外部化配置

聲明:本網(wǎng)頁內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問題請及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com

上一篇: 實(shí)用!Python數(shù)據(jù)可視化與圖表繪制:讓數(shù)據(jù)一目了然

下一篇: Golang 中的 bufio 包詳解之Bufio.Writer

標(biāo)簽:
  • 熱門焦點(diǎn)
Top