Spring源码(四)-bean的加载(上)
前言
前面讲述了context的创建,接下来将进入到核心的bean的创建过程,前期的准备工作已经完成,相信很多人跟我一样,看过了一遍只能有个大概的印象,接下来有时间的话我会考虑结合UML和脑图这样的出来和大家一起分享,有经验或者想一起学习的,希望可以联系我,qq:616516146,言归正传,回到代码。
1、prepareContext()
接下来回到最初代码SpringApplication中run方法,我们先看prepareContext()这个方法。该方法是做context的准备工作。我们进入代码中看下他的具体实现。
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
/**
* 该方法对context进行了预设置,设置了ResourceLoader和ClassLoader,
* 并向bean工厂中添加了一个beanNameGenerator
*/
postProcessApplicationContext(context);
/**
*
* applyInitializers(context)方法
* 获取到了我们或spring通过SpringApplication.setInitializers(xxx)
* 设置的应用上下文初始化器集合
*/
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// Load the sources
Set<Object> sources = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
/**
* 将各种bean装载进context对象中
*/
load(context, sources.toArray(new Object[sources.size()]));
listeners.contextLoaded(context);
}
我们主要只讨论 postProcessApplicationContext(context)、 applyInitializers(context)以及 load(context, sources.toArray(new Object[sources.size()]))这三个方法
- 1、postProcessApplicationContext(context)
该方法对context进行了预设置,设置了ResourceLoader和ClassLoader, 并向bean工厂中添加了一个beanNameGenerator - 2、applyInitializers(context)
该方法获取到了我们或spring通过SpringApplication.setInitializers(xxx)设置的应用上下文初始化器集合。 - 3、load(context, sources.toArray(new Object[sources.size()]))
这个方法主要是加载各种beans到context对象中的。sources代表各种资源对象,然后BeanDefinitionLoader的内部通过各种xxxReader和xxxScanner读取、解析这些资源对象中的beans
/**
* Load beans into the application context.
* @param context the context to load beans into
* @param sources the sources to load
* 这个方法主要是加载各种beans到context对象中的。
* sources代表各种资源对象,然后BeanDefinitionLoader
* 的内部通过各种xxxReader和xxxScanner读取、解析这些资源对象中的beans。
* 具体细节在 BeanDefinitionLoader中实现
*/
protected void load(ApplicationContext context, Object[] sources) {
logger.debug("开始执行load方法");
System.out.println("-------------开始执行load方法");
if (logger.isDebugEnabled()) {
logger.debug(
"Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
}
BeanDefinitionLoader loader = createBeanDefinitionLoader(
getBeanDefinitionRegistry(context), sources);
if (this.beanNameGenerator != null) {
loader.setBeanNameGenerator(this.beanNameGenerator);
}
if (this.resourceLoader != null) {
loader.setResourceLoader(this.resourceLoader);
}
if (this.environment != null) {
loader.setEnvironment(this.environment);
}
loader.load();
}
以上是load方法加载。
上面三个方法完成之后,prepareContext()函数已经准备好了refresh上下文的基础准备工作。,接下来看refresh的相关工作。
2、refresh
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
/**
* 又注册了关闭context时的钩子
*/
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
这里我们进入refresh看看里面的具体实现,此处的refrsh调用的是AbstractApplicationContext的refrsh方法,之后又调用了context.registerShutdownHook();关闭了钩子。进入到AbstractApplicationContext,看下refrsh的具体实现,这里只截取了一部分代码
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
//BeanFactory创建
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.
//DefaultListableBeanFactory,完成basepackage的扫描
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.
//DefaultListableBeanFactory
//该方法进行了非懒加载beans的初始化工作
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
这里我们只重点关注 finishBeanFactoryInitialization(beanFactory)这个方法。
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// Initialize conversion service for this context.
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// Register a default embedded value resolver if no bean post-processor
// (such as a PropertyPlaceholderConfigurer bean) registered any before:
// at this point, primarily for resolution in annotation attribute values.
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}
// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
logger.info("----------weaverAwareName:"+weaverAwareName);
//创建bean的实例
getBean(weaverAwareName);
}
// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);
// Allow for caching all bean definition metadata, not expecting further changes.
beanFactory.freezeConfiguration();
// Instantiate all remaining (non-lazy-init) singletons.
//此处beanFactory是DefaultListableBeanFactory,获取bean
beanFactory.preInstantiateSingletons();
}
最后调用了beanFactory的preInstantiateSingletons方法,此处的beanFactory是DefaultListableBeanFactory。那么我们就看下这个的重点。
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
this.logger.info("-----beanName:"+beanName);
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
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()方法,别的有兴趣和时间再研究吧,先忽略别的,仔细研究getBean()方法,经过追踪发现,他调用了AbstractoryFactory的doGetBean()方法,这里是一大段的代码。这里面有太多的代码,我们先简单描述,下节继续深入。
//实例化依赖的bean之后可以实例化mbd本身了
//单例模式的创建
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
/**
*核心创建bean
*/
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的实现,Spring默认的bean都是单例的,在此我们看到了createBean()方法,spring源码写的确实是比较优秀,根据方法名就能大致的知道其作用。这里的createBean()方法使用的是其子类的方法,这里摘取一点代码片段看下
/**
* 锁定class,根据设置的class属性或者根据classname来解析class
*/
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
到此bean加载和创建的基本流程也就结束了,下一篇我们着重会分析一些具体的细节。
代码的构建请参考 github
该地址有相应代码的注释