Spring中Bean的加载与SpringBoot的初始化流程

前言:一直对它们之间的关系感到好奇,SpringBoot既然是Spring的封装,那么SpringBoot在初始化时应该也会有Bean的加载,那么是在何时进行加载的呢?

参考:

https://blog.csdn.net/qq_36688143/article/details/84968036

https://blog.csdn.net/q610376681/article/details/87928766

https://blog.csdn.net/q610376681/article/details/88617026

目录

第一章 Spring中Bean的一些简单概念

1.1 SpingIOC简介

1.2 BeanFactory

1.2.1 BeanDefinition

1.2.2 BeanDefinitionRegistry

1.2.3 BeanFactory结构图

1.3 ApplicationContext

第二章 SpringBoot的初始化流程

2.1 准备阶段

2.2 运行阶段

2.2.1 监听器分析

2.2.2 refreshContext

2.3 总结


第一章 Spring中Bean的一些简单概念

1.1 SpingIOC简介

Spring启动时去读取应用程序提供的Bean配置信息,并在Spring容器中生成相应的Bean定义注册表,然后根据注册表去实例化Bean,装配好Bean之间的依赖关系,为上层提供准备就绪的运行环境.

Spring提供一个配置文件描述Bean与Bean之间的依赖关系,利用Java语言的反射功能实例化Bean,并建立Bean之间的依赖关系.

1.2 BeanFactory

 BeanFactory是接口,提供了IOC容器最基本的形式,给具体的IOC容器的实现提供了规范。

1.2.1 BeanDefinition

主要用来描述Bean的定义,Spring在启动时会将Xml或者注解里Bean的定义解析成Spring内部的BeanDefinition.

beanClass保存bean的class属性,scop保存bean是否单例,abstractFlag保存该bean是否抽象,lazyInit保存是否延迟初始化,autowireMode保存是否自动装配,等等等

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注册到BeanFactory接口的实现类DefaultListableBeanFacory中的beanDefinitionMap中。

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

1.2.3 BeanFactory结构图

ListableBeanFactory该接口定义了访问容器中Bean的若干方法,如查看Bean的个数,获取某一类型Bean的配置名,查看容器中是否包括某一Bean等方法.

HierarchicalBeanFactory是父子级联的IOC容器接口,子容器可以通过接口方法访问父容器,通过HierarchicalBeanFactory接口SpringIOC可以建立父子层级关联的IOC层级体系,子容器可以访问父容器的Bean,父容器不能访问子容器的Bean,比如展现层的Bean位于子容器中而业务层和持久层的Bean位于父容器的Bean.

ConfigurableBeanFactory:增强了IOC接口的可定制性,定义了设置类装载器,属性遍历器,以及属性初始化后置处理器等方法.

AutowireCapableBeanFactory:定义了将容器中的Bean按某种规则,按名字匹配,按类型匹配等.

SingletonBeanRegistry:允许在运行期间向容器注册SingletonBean实例的方法.

通过这些接口也证明了BeanFactory的体系也确实提供了IOC的基础及依赖注入和Bean的装载等功能.

 

1.3 ApplicationContext

由于BeanFactory的功能还不够强大,于是Spring在BeanFactory的基础上还设计了一个更为高级的接口即ApplicationContext,它是BeanFactory的子接口之一.在我们使用SpringIOC容器时,大部分都是context的实现类。

我理解着就是BeanFactory只提供IOC,ApplicationContext还提供很多别的功能。

 

第二章 SpringBoot的初始化流程

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

SpringApplication的run分为两个阶段,即new SpringApplication()时的执行构造函数的准备阶段,和run时的运行阶段。

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

2.1 准备阶段

在准备阶段会

配置SpringBean的来源

推断web应用类型

加载应用上下文初始器

加载应用事件监听器

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应用类型
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
        //加载应用上下文初始化器
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
        //加载应用事件监听器
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

 

2.2 运行阶段

加载:SpringApplication获得监听器

运行:SpringApplication运行监听器

监听:SpringBoot事件、Spring事件

创建:应用上下文、Enviroment、其它(不重要),应用上下文创建后会被应用上下文初始化器初始化,Enviroment是抽象的环境对象。

失败:故障分析报告。

回调:CommandLineRunner、ApplicationRunner

public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
        //获得监听器
		SpringApplicationRunListeners listeners = getRunListeners(args);
        //运行监听器
		listeners.starting();
		try {
            //应用上下文
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
            //环境
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
            //依据不同的配置加载不同的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 监听器分析

这个是看了源码后的个人理解,不保证一定正确,只提供一定的参考。

在准备阶段加载实现了ApplicationListener的监听器。

然后在运行阶段调用了starting()方法。

//获得监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
//运行监听器
listeners.starting();

下面是listeners的源码,可以看到它的每一个方法,都对应SpringBoot的一个阶段,这表明每到对应的阶段,都要广播对应的事件。

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);
		}
	}

	//省略...

}

那么问题来了,广播事件后,事件是怎么被监听到的呢?我们打开listener.environmentPrepared(environment)的源码,发现其调用了initialMulticaster进行了事件广播

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

 广播的代码如下,看了一下感觉大意就是找到根事件匹配的监听器,然后调用线程池去执行对应的触发函数。

@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方法,一直点进去可以看到如下:

prepareRefresh:完成配置之类的解析,设置Spring的状态,初始化属性源信息,验证环境信息中必须存在的属性.

ConfigurableListableBeanFactory:是用来获取beanFactory的实例的(第一张也写过BeanFactory负责bean的加载与获取)。

PrepareBeanFactory:对beanFactory进行相关的设置,为后续的使用做准备,包括设置classLoader用来加载Bean,设置表达式解析器等等.

postProcessBeanFactory:是用于在BeanFactory设置之后进行后续的BeanFactory的操作.

invokeBeanFactoryPostProcessors:点进去发现调用了如下方法,点进去之后发现逻辑相当复杂,主要调用工厂后处理器,调用Bean标签,扫描Bean文件,并解析成一个个的Bean,这时候这些Bean是被加载进了Spirng容器当中,这里涉及了各种类,我们在这里主要说一下ConfigurationClassParser,主要是解析Bean的类.该方法会对带有@configuration,@import,@bean,以及@SpringBootApplication等标签的Bean进行解析,

registerBeanPostProcessors:会从Spring容器中找出实现BeanPostProcessors接口的Bean,并设置到BeanFactory的属性之中,之后Bean实例化时会调用BeanProcessor,也就是Bean的后置处理器.会和AOP比较相关.

initMessageSource:初始化消息源(这个自己推断的)

initApplicationEventMuticaster:初始化事件广播器

onRefresh:是一个模板方法,不同的Spring容器会重写它做不同的事情.比如web程序的容器,会调用create..方法去创建内置的servlet容器.

registerListeners:注册事件监听器

finishBeanFactoryInitialization:会实例化BeanFactory中已被注册但未被实例化的所有实例,懒加载是不需要被实例化的.前面的invokeBeanFactoryPostProcessors方法中根据各种注解解析出来的Bean在这个时候都会被初始化,同时初始化过程中的各种PostProcessor就会开始起作用了.

finishRefresh:会做初始化生命周期处理器相关的事情.

@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();
			}
		}
	}

当上面的代码执行完毕后,返回上层代码,后面是注册钩子,这钩子是希望开发者能结合自己的实际需求扩展出一些在Spring容器关闭时的行为.

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

 继续返回上层代码,可以看到afterRefresh方法它的方法体是空的, 也就说明Spring框架考虑了扩展性,留了很多的口子,让大家在框架层面继承很多的模块并去做自定义的实现

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

2.3 总结

总的来说,SpringBoot加载的Bean的时机为,点进一开始的run方法,层层递进后,由

refreshContext(context);

进行了bean的加载,更详细的话,那就层层递进点进去,是在如下方法进行了bean的加载。

invokeBeanFactoryPostProcessors(beanFactory);

 

 

 

 

  • 2
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值