SpringBoot(十四)启动流程分析之refreshContext()

本文深入分析了SpringBoot启动过程中的refreshContext()方法,详细讲解了从准备刷新、bean工厂的准备、BeanFactoryPostProcessor与BeanPostProcessor的调用,到初始化信息源、事件多播等关键步骤,揭示了SpringBoot内部运行机制。
摘要由CSDN通过智能技术生成

SpringBoot版本:2.1.1     ==》启动流程分析汇总

接上篇Spring Boot 2.1.1(十三)启动流程分析之准备应用上下文

目录

流程分析 

1、准备刷新

 子类prepareRefresh()方法

父类prepareRefresh()方法

2、通知子类刷新内部bean工厂

3、准备bean工厂

4、允许上下文子类对bean工厂进行后置处理

5、调用已注册的BeanFactoryPostProcessors Bean

先调用BeanDefinitionRegistryPostProcessors

再调用BeanFactoryPostProcessor

6、注册BeanPostProcessor

7、初始化信息源

8、注册SimpleApplicationEventMulticaster

9、onRefresh()

10、注册Listener

11、完成beanFactory初始化,初始化所有剩余的单例bean

12、完成刷新


public ConfigurableApplicationContext run(String... args) {
            .... 
	try {
            //本篇内容从本行开始记录  
            refreshContext(context);
           //本篇内容记录到这,后续更新
            ....
        }
	catch (Throwable ex) {
		handleRunFailure(context, ex, exceptionReporters, listeners);
		throw new IllegalStateException(ex);
	}
}

流程分析 

感觉这篇内容会有很多呀!怼它!来看源码。

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

最终调用父类AbstractApplicationContext的refresh()方法。 

protected void refresh(ApplicationContext applicationContext) {
	Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
	((AbstractApplicationContext) applicationContext).refresh();
}
@Override
public final void refresh() throws BeansException, IllegalStateException {		
    try {
	     super.refresh();
    }
    catch (RuntimeException ex) {
	    stopAndReleaseWebServer();
	    throw ex;
    }
}

 可以看到refresh中的步骤都是单个单个的方法,很方便看,下面一个一个方法讲。

@Override
public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
	    // 准备刷新
	    prepareRefresh();

	    // 通知子类刷新内部bean工厂
	    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

	    // 准备bean工厂以便在此上下文中使用
	    prepareBeanFactory(beanFactory);

	    try {
	    	// 允许上下文子类中对bean工厂进行后处理
	    	postProcessBeanFactory(beanFactory);

	    	// 在bean创建之前调用BeanFactoryPostProcessors后置处理方法
	    	invokeBeanFactoryPostProcessors(beanFactory);

	    	// 注册BeanPostProcessor
	    	registerBeanPostProcessors(beanFactory);

	    	// 注册DelegatingMessageSource
	    	initMessageSource();

	    	// 注册multicaster
	    	initApplicationEventMulticaster();

	    	// 创建内置的Servlet容器
	    	onRefresh();

	    	// 注册Listener
	    	registerListeners();

	    	// 完成BeanFactory初始化,初始化剩余单例bean
	    	finishBeanFactoryInitialization(beanFactory);

	    	// 发布对应事件
	    	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();
	    }
    }
}

开始之前先把AnnotationConfigServletWebServerApplicationContext类图放这:

1、准备刷新

先调用子类重写的方法,再调用父类方法。还记得前面讲过在创建AnnotationConfigServletWebServerApplicationContext的时候构造方法中实例化了一个ClassPathBeanDefinitionScanner。

@Override
protected void prepareRefresh() {
	this.scanner.clearCache();
	super.prepareRefresh();
}

 子类prepareRefresh()方法

 在其父类ClassPathScanningCandidateComponentProvider中有一个MetadataReaderFactory(接口)工厂对象,判断该对象是否是CachingMetadataReaderFactory这个特定类或者是它的子类的一个实例,返回Boolean值,是就是true,则清除缓存。该类中有个Map,用来缓存每个Spring资源句柄(即每个“.class”文件)的MetadataReader实例。

缓存Map如果是LocalResourceCache(可以看到该类继承了LinkedHashMap),执行的就是LinkedHashMap的clear()方法了;

else如果缓存不为空,就是重新new一个LocalResourceCache。

父类prepareRefresh()方法

protected void prepareRefresh() {
        //系统启动时间
	this.startupDate = System.currentTimeMillis();
        //是否关闭标识,false
	this.closed.set(false);
        //是否活跃标识,true
	this.active.set(true);

	if (logger.isDebugEnabled()) {
		if (logger.isTraceEnabled()) {
			logger.trace("Refreshing " + this);
		}
		else {
			logger.debug("Refreshing " + getDisplayName());
		}
	}

	// 调用子类GenericWebApplicationContext重写后的方法替换servlet相关属性源
	initPropertySources();

	
	// 这里是验证由ConfigurablePropertyResolver#setRequiredProperties()方法指定的属性,解析为非空值,如果没有设置的话这个方法就不会执行什么操作。
	getEnvironment().validateRequiredProperties();

	// Allow for the collection of early ApplicationEvents,
	// to be published once the multicaster is available...
	this.earlyApplicationEvents = new LinkedHashSet<>();
}

 看下最后这个方法,initServlet属性源,方法注释是这么说的将基于Servlet的StubPropertySource替换为使用给定servletContext和servletConfig对象填充的实例。此方法可以被调用任意次数,但将用相应的实际属性源替换为StubPropertySource一次且仅一次。

看下if判断里的条件,servletContext不为空,source中存在指定name的的属性源,且该属性源要是StubPropertySource的类型或者是其子类。也就是当第一次调用以后,该属性源就被替换成了ServletContextPropertySource和ServletConfigPropertySource,所以之后的调用最后一个判断条件就不会成立了。

public static void initServletPropertySources(MutablePropertySources sources,
			@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {

	Assert.notNull(sources, "'propertySources' must not be null");
        //servletContextInitParams
	String name = StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME;
	if (servletContext != null && sources.contains(name) && sources.get(name) instanceof StubPropertySource) {
		sources.replace(name, new ServletContextPropertySource(name, servletContext));
	}
        //servletConfigInitParams
	name = StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME;
	if (servletConfig != null && sources.contains(name) && sources.get(name) instanceof StubPropertySource) {
		sources.replace(name, new ServletConfigPropertySource(name, servletConfig));
	}
}

 看validateRequiredProperties()方法前,先看下Environment的类图。这里是验证由ConfigurablePropertyResolver#setRequiredProperties()方法指定的属性,解析为非空值,如果没有设置的话这个方法就不会执行什么操作,所以这里就略过了。

方法最后new了一个LinkedHashSet,收集早期事件,如果multicaster 有用就会广播事件。

prepareRefresh()就执行完了。

2、通知子类刷新内部bean工厂

 可以看到refreshBeanFactory()方法上的注释说的,什么都不做:我们拥有一个内部beanfactory,并依靠调用方通过我们的公共方法(或beanfactory)注册bean。

前面介绍GenericApplicationContext说了与每次刷新创建新的内部beanfactory实例的其他applicationContext实现不同,此上下文的内部beanfactory从一开始就可用,以便能够在其上注册bean定义。只能调用一次refresh()。

所以在该方法内只设置了SerializationId,该id是在准备应用上下文时调用ContextIdApplicationContextInitializer时设置的id,在setSerializationId方法中,使用id做key,new了一个弱引用对象为value,添加到了serializableFactories中,DefaultListableBeanFactory为被弱引用对象;如果需要,可以通过id得到引用对象,在通过get()方法得到DefaultListableBeanFactory对象。

 

3、准备bean工厂

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
	//通知内部bean工厂使用上下文的类加载器
	beanFactory.setBeanClassLoader(getClassLoader());
   
SpringBoot2的启动流程是通过@SpringBootApplication注解自动化配置来实现的。该注解包含了多个注解的组合,其中包括@ComponentScan、@EnableAutoConfiguration和@Configuration等。通过这些注解,Spring Boot会自动扫描并加载配置类,并根据自动配置规则来配置应用程序。 具体而言,当应用程序启动时,Spring Boot会创建一个Spring应用程序上下文。在创建上下文的过程中,会先加载主配置类(通常是包含main方法的类),然后根据@ComponentScan注解扫描指定包下的所有组件。 接下来,Spring Boot会根据@EnableAutoConfiguration注解自动配置应用程序。这个注解会根据classpath和条件匹配的规则,加载配置类,并将它们注册到应用程序上下文中。这些配置类使用了@Configuration注解,会声明一些Bean,并根据条件来决定是否生效。 最后,Spring Boot会启动应用程序,并执行相应的事件处理器。这些事件处理器可以通过自定义ApplicationListener来实现。在应用程序运行期间,Spring Boot会触发不同的事件,并调用相应的事件处理器。 参考文献: 引用:SpringBoot2 | @SpringBootApplication注解 自动化配置流程源码分析(三) [2] 引用:SpringBoot2 | SpringBoot监听器源码分析 | 自定义ApplicationListener(六) 引用:该系列主要还是Spring的核心源码,不过目前Springboot大行其道,所以就从Springboot开始分析。最新版本是Springboot2.0.4,Spring5,所以新特性本系列后面也会着重分析
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值