面试官问Spring中Bean如何完成加载,你准备怎么回答之上集?
前言
前面讲述了context
的创建,接下来将进入到核心的bean
的创建过程,前期的准备工作已经完成,相信很多人跟我一样,看过了一遍只能有个大概的印象,接下来有时间的话我会考虑结合UML
和脑图这样的出来和大家一起分享。
1、prepareContext()
接下来回到最初代码SpringApplication
中run
方法,我们先看prepareContext()
这个方法。该方法是做context
的准备工作。我们进入代码中看下他的具体实现。
1private void prepareContext(ConfigurableApplicationContext context,
2 ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
3 ApplicationArguments applicationArguments, Banner printedBanner) {
4 context.setEnvironment(environment);
5 /**
6 * 该方法对context进行了预设置,设置了ResourceLoader和ClassLoader,
7 * 并向bean工厂中添加了一个beanNameGenerator
8 */
9
10 postProcessApplicationContext(context);
11 /**
12 *
13 * applyInitializers(context)方法
14 * 获取到了我们或spring通过SpringApplication.setInitializers(xxx)
15 * 设置的应用上下文初始化器集合
16 */
17 applyInitializers(context);
18 listeners.contextPrepared(context);
19 if (this.logStartupInfo) {
20 logStartupInfo(context.getParent() == null);
21 logStartupProfileInfo(context);
22 }
23
24 // Add boot specific singleton beans
25 context.getBeanFactory().registerSingleton("springApplicationArguments",
26 applicationArguments);
27 if (printedBanner != null) {
28 context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
29 }
30
31 // Load the sources
32 Set<Object> sources = getSources();
33 Assert.notEmpty(sources, "Sources must not be empty");
34 /**
35 * 将各种bean装载进context对象中
36 */
37 load(context, sources.toArray(new Object[sources.size()]));
38 listeners.contextLoaded(context);
39}
我们主要只讨论 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
1 /**
2 * Load beans into the application context.
3 * @param context the context to load beans into
4 * @param sources the sources to load
5 * 这个方法主要是加载各种beans到context对象中的。
6 * sources代表各种资源对象,然后BeanDefinitionLoader
7 * 的内部通过各种xxxReader和xxxScanner读取、解析这些资源对象中的beans。
8 * 具体细节在 BeanDefinitionLoader中实现
9 */
10 protected void load(ApplicationContext context, Object[] sources) {
11 logger.debug("开始执行load方法");
12 System.out.println("-------------开始执行load方法");
13 if (logger.isDebugEnabled()) {
14 logger.debug(
15 "Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
16 }
17 BeanDefinitionLoader loader = createBeanDefinitionLoader(
18 getBeanDefinitionRegistry(context), sources);
19 if (this.beanNameGenerator != null) {
20 loader.setBeanNameGenerator(this.beanNameGenerator);
21 }
22 if (this.resourceLoader != null) {
23 loader.setResourceLoader(this.resourceLoader);
24 }
25 if (this.environment != null) {
26 loader.setEnvironment(this.environment);
27 }
28 loader.load();
29 }
以上是load
方法加载。
上面三个方法完成之后,prepareContext()
函数已经准备好了refresh
上下文的基础准备工作。接下来看refresh
的相关工作。
2、refresh
1private void refreshContext(ConfigurableApplicationContext context) {
2 refresh(context);
3 if (this.registerShutdownHook) {
4 try {
5 /**
6 * 又注册了关闭context时的钩子
7 */
8 context.registerShutdownHook();
9 }
10 catch (AccessControlException ex) {
11 // Not allowed in some environments.
12 }
13 }
14}
这里我们进入refresh
看看里面的具体实现,此处的refrsh
调用的是AbstractApplicationContext
的refrsh
方法,之后又调用了context.registerShutdownHook();
关闭了钩子。进入到AbstractApplicationContext
,看下refrsh
的具体实现,这里只截取了一部分代码
1synchronized (this.startupShutdownMonitor) {
2 // Prepare this context for refreshing.
3 prepareRefresh();
4 // Tell the subclass to refresh the internal bean factory.
5 //BeanFactory创建
6 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
7 // Prepare the bean factory for use in this context.
8 prepareBeanFactory(beanFactory);
9 try {
10 // Allows post-processing of the bean factory in context subclasses.
11 postProcessBeanFactory(beanFactory);
12 // Invoke(调用) factory processors registered as beans in the context.
13 //DefaultListableBeanFactory,完成basepackage的扫描
14 invokeBeanFactoryPostProcessors(beanFactory);
15 // Register bean processors that intercept bean creation.
16 registerBeanPostProcessors(beanFactory);
17 // Initialize message source for this context.
18 initMessageSource();
19 // Initialize event multicaster for this context.
20 initApplicationEventMulticaster();
21 // Initialize other special beans in specific context subclasses.
22 onRefresh();
23 // Check for listener beans and register them.
24 registerListeners();
25 // Instantiate all remaining (non-lazy-init) singletons.
26 //DefaultListableBeanFactory
27 //该方法进行了非懒加载beans的初始化工作
28 finishBeanFactoryInitialization(beanFactory);
29 // Last step: publish corresponding event.
30 finishRefresh();
31 }
这里我们只重点关注 finishBeanFactoryInitialization(beanFactory)
这个方法。
1protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
2 // Initialize conversion service for this context.
3 if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
4 beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
5 beanFactory.setConversionService(
6 beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
7 }
8
9 // Register a default embedded value resolver if no bean post-processor
10 // (such as a PropertyPlaceholderConfigurer bean) registered any before:
11 // at this point, primarily for resolution in annotation attribute values.
12 if (!beanFactory.hasEmbeddedValueResolver()) {
13 beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
14 }
15
16 // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
17 String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
18 for (String weaverAwareName : weaverAwareNames) {
19 logger.info("----------weaverAwareName:"+weaverAwareName);
20 //创建bean的实例
21 getBean(weaverAwareName);
22 }
23
24 // Stop using the temporary ClassLoader for type matching.
25 beanFactory.setTempClassLoader(null);
26
27 // Allow for caching all bean definition metadata, not expecting further changes.
28 beanFactory.freezeConfiguration();
29
30 // Instantiate all remaining (non-lazy-init) singletons.
31 //此处beanFactory是DefaultListableBeanFactory,获取bean
32 beanFactory.preInstantiateSingletons();
33}
最后调用了beanFactory
的preInstantiateSingletons
方法,此处的beanFactory
是DefaultListableBeanFactory
。那么我们就看下这个的重点。
1List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
2// Trigger initialization of all non-lazy singleton beans...
3for (String beanName : beanNames) {
4 this.logger.info("-----beanName:"+beanName);
5 RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
6 if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
7 if (isFactoryBean(beanName)) {
8 final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
9 boolean isEagerInit;
10 if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
11 isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) () ->
12 ((SmartFactoryBean<?>) factory).isEagerInit(),
13 getAccessControlContext());
14 }
15 else {
16 isEagerInit = (factory instanceof SmartFactoryBean &&
17 ((SmartFactoryBean<?>) factory).isEagerInit());
18 }
19 if (isEagerInit) {
20 getBean(beanName);
21 }
22 }
23 else {
24 /**
25 * 核心代码
26 */
27 getBean(beanName);
28 }
29 }
30}
在这里看到了熟悉的getBean()
方法,别的有兴趣和时间再研究吧,先忽略别的,仔细研究getBean()
方法,经过追踪发现,他调用了AbstractoryFactory
的doGetBean()
方法,这里是一大段的代码。这里面有太多的代码,我们先简单描述,下节继续深入。
1//实例化依赖的bean之后可以实例化mbd本身了
2//单例模式的创建
3if (mbd.isSingleton()) {
4 sharedInstance = getSingleton(beanName, () -> {
5 try {
6 /**
7 *核心创建bean
8 */
9 return createBean(beanName, mbd, args);
10 }
11 catch (BeansException ex) {
12 // Explicitly remove instance from singleton cache: It might have been put there
13 // eagerly by the creation process, to allow for circular reference resolution.
14 // Also remove any beans that received a temporary reference to the bean.
15 destroySingleton(beanName);
16 throw ex;
17 }
18 });
这里是单例bean
的实现,Spring
默认的bean
都是单例的,在此我们看到了createBean()
方法,spring
源码写的确实是比较优秀,根据方法名就能大致的知道其作用。这里的createBean()
方法使用的是其子类的方法,这里摘取一点代码片段看下
1/**
2 * 锁定class,根据设置的class属性或者根据classname来解析class
3 */
4Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
5
6if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
7 mbdToUse = new RootBeanDefinition(mbd);
8 mbdToUse.setBeanClass(resolvedClass);
9}
到此bean
加载和创建的基本流程也就结束了,下一篇我们着重会分析一些具体的细节。
代码的构建请参考 github
该地址有相应代码的注释
Spring源码分析
限时扫码关注领取600页+Spring官方教程小书
限时3周免费下载
长按识别二维码关注SpringForAll社区公众号
在公众号回复“Spring”或者"spring"即可获取
点击原文阅读更多