本章节主要介绍,面试中经常问到的Spring-Bean初始化过程。
接下来会通过以下代码,进行debug调试,讲解(spring版本为5.3.23,源码太长会转换为图片,节省字数,提高阅读性)。
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Test bean = applicationContext.getBean(Test.class);
bean.a();
applicationContext.close();
/** Output:
* 初始化
* before aspect
* 方法调用
* 销毁
*/
}
public void a(){ System.out.println("方法调用"); }
public void init(){ System.out.println("初始化"); }
public void destroy(){ System.out.println("销毁"); }
}
public class MyAspect {
public void before() {
System.out.println("before aspect");
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="test" class="com.example.Test" init-method="init" destroy-method="destroy">bean>
<bean id="myAspect" class="com.example.MyAspect">bean>
<aop:config>
<aop:aspect ref="myAspect">
<aop:before method="before" pointcut="execution(* com.example.Test.a(..))">aop:before>
aop:aspect>
aop:config>
beans>
首先进入构造方法,会设置默认值。
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[]{configLocation}, true, (ApplicationContext)null);
}
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
super(parent);
this.setConfigLocations(configLocations);
if (refresh) {
this.refresh();
}
}
紧接着调用,super()方法,最终调用AbstractApplicationContext类的构造方法,进行初始化配置,创建标准环境(占位符、系统参数)等。

然后进入核心refresh()方法
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
//启动步骤
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
//省略部分代码... ...
}
}
调用prepareRefresh()方法,准备刷新

执行第一个else代码块中逻辑,打印了第一条日志,然后初始化监听器、事件。

然后进入obtainFreshBeanFactory()方法
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
this.refreshBeanFactory();
return this.getBeanFactory();
}
先调用AbstractRefreshableApplicationContext.refreshBeanFactory()方法,
protected final void refreshBeanFactory() throws BeansException {
//省略部分代码... ...
try {
DefaultListableBeanFactory beanFactory = this.createBeanFactory();
beanFactory.setSerializationId(this.getId());
//省略部分代码... ...
} catch (IOException var2) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var2);
}
}
执行 createBeanFactory()方法创建bean工厂,往下走初始化bean的一些容器。

初始化bean容器完成。
bean工厂创建完毕后,继续往下走,调用AbstractBeanDefinitionReader.loadBeanDefinitions()方法
protected final void refreshBeanFactory() throws BeansException {
try {
DefaultListableBeanFactory beanFactory = this.createBeanFactory();
beanFactory.setSerializationId(this.getId());
this.customizeBeanFactory(beanFactory);
this.loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
} catch (IOException var2) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var2);
}
}
通过getResources()方法,获取上下文的资源(Resource对象包含的参数很多,比如jar包、类路径啥的)
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = this.getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException("Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
} else {
int count;
if (resourceLoader instanceof ResourcePatternResolver) {
try {
//获取上下文资源,包含path和上下文资源
Resource[] resources = ((ResourcePatternResolver)resourceLoader).getResources(location);
count = this.loadBeanDefinitions(resources);
if (actualResources != null) {
Collections.addAll(actualResources, resources);
}
if (this.logger.isTraceEnabled()) {
this.logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
}
return count;
} catch (IOException var6) {
throw new BeanDefinitionStoreException("Could not resolve bean definition resource pattern [" + location + "]", var6);
}
}
//省略部分代码...
}
}
然后继续往下走,调用XmlBeanDefinitionReader.loadBeanDefinitions()方法
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
//省略部分代码...
else {
int var6;
try {
InputStream inputStream = encodedResource.getResource().getInputStream();
Throwable var4 = null;
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
var6 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
} catch (Throwable var24) {
var4 = var24;
throw var24;
}
//省略部分代码... ...
return var6;
}
}
我们可以看下是如何获取InputStream的,我们可以找到子类ClassPathResource
public InputStream getInputStream() throws IOException {
InputStream is;
if (this.clazz != null) {
is = this.clazz.getResourceAsStream(this.path);
} else if (this.classLoader != null) {
is = this.classLoader.getResourceAsStream(this.path);
} else {
is = ClassLoader.getSystemResourceAsStream(this.path);
}
if (is == null) {
throw new FileNotFoundException(this.getDescription() + " cannot be opened because it does not exist");
} else {
return is;
}
}

也就是说通过path参数从ClassLoader中寻找文件,有点类似于File获取文件。
实际点开InputSource可以发现,寻找的路径为target编译文件中。

获取文件流后,进入doLoadBeanDefinitions()方法,
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
try {
Document doc = this.doLoadDocument(inputSource, resource);
int count = this.registerBeanDefinitions(doc, resource);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
}
加载bean信息(这里就不细讲过程了,直接贴图),可以看到把xml里面的属性和值都解析出来了(可以看下fNodeName对象和fNodeValue对象,此时BeanDefinition中的数量依旧是0)

接着往下走进入DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions(Element)方法
protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = this.createDelegate(this.getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute("profile");
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");
if (!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + this.getReaderContext().getResource());
}
return;
}
}
}
this.preProcessXml(root);
this.parseBeanDefinitions(root, this.delegate);
this.postProcessXml(root);
this.delegate = parent;
}
介绍下root元素包含的一些属性(attributes属性和firstChild属性,结合xml文件看更方便理解),如图:

调用createDelegate()方法,创建一个解析器委托(不详细讲解),往下走DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(Element , BeanDefinitionParserDelegate)方法先判断是不是默认命名空间,返回true,从根节点元素开始,获取根节点的子节点列表,进入循环,第一次是空格,第二次返回一个bean,判断是元素,是默认命名空间往下走,进入parseDefaultElement()方法


判断解析的元素是那种类型,这里进入bean判断,往下走最后进入BeanDefinitionParserDelegate .parseBeanDefinitionElement(Element,BeanDefinition)方法
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, "import")) {
this.importBeanDefinitionResource(ele);
} else if (delegate.nodeNameEquals(ele, "alias")) {
this.processAliasRegistration(ele);
} else if (delegate.nodeNameEquals(ele, "bean")) {
this.processBeanDefinition(ele, delegate);
} else if (delegate.nodeNameEquals(ele, "beans")) {
this.doRegisterBeanDefinitions(ele);
}
}
详细解析看注释

最后调用parseBeanDefinitionElement(Element, String, @Nullable BeanDefinition containingBean)重载方法,详细解析看注释

特别讲解下上图中createBeanDefinition()方法,生成抽象AbstractBeanDefinition类(一个标签对应所有元素的类), 设置相对应的类路径。
public static AbstractBeanDefinition createBeanDefinition(@Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setParentName(parentName);
if (className != null) {
if (classLoader != null) {
bd.setBeanClass(ClassUtils.forName(className, classLoader));
} else {
//设置bean的类名
bd.setBeanClassName(className);
}
}
return bd;
}
创建好AbstractBeanDefinition类,往下走调用parseBeanDefinitionAttributes()方法,开始解析bean标签的属性,进行属性设置,具体参考代码注释。

解析完毕,回到上层parseBeanDefinitionElement(Element, String, @Nullable BeanDefinition containingBean)
(解析注入方法),再回到最上层parseBeanDefinitionElement(Element,BeanDefinition)方法,可以看到相应的属性以代码进行赋值。

最终new BeanDefinitionHolder()设置bean容器持有对象,赋值名称,别名。

返回到上一层processBeanDefinition()方法,BeanDefinitionHolder元素解析完毕,不为空,进入if判断
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
//修饰后重新赋值
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
//循环bean的所有元素,进行修饰
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());
} catch (BeanDefinitionStoreException var5) {
this.getReaderContext().error("Failed to register bean definition with name '"
+ bdHolder.getBeanName() + "'", ele, var5);
}
this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
调用DefaultListableBeanFactory.registerBeanDefinition()方法,进入else代码块,给beanDefinitionMap和beanDefinitionNames插入数据

到此第一个bean实例化的操作已经完成,紧接着第二个bean:myAspect,按照上述逻辑进行注册。
进入循环下一个标签,如图所示,进行切面的实例化。

依旧是按照原有实例化bean的逻辑进行。
循环下一个标签,判断不是bean的空间,进入else代码块,解析自定元素,如图所示

进入parseCustomElement()方法,先获取命名空间URL,不为空进入else代码块,

进入resolve()方法,通过命名空间的URL找到对应的类

NamespaceHandler不为空进入else代码块,调用parse()方法,最终进入ConfigBeanDefinitionParser.parse()方法解析,调用configureAutoProxyCreator()方法,配置自动代理创建器

进入方法,往下一直走调用AopConfigUtils.registerOrEscalateApcAsRequired()方法
public static void registerAutoProxyCreatorIfNecessary(ParserContext parserContext, Element sourceElement) {
//注册自动代理
BeanDefinition beanDefinition = AopConfigUtils.registerAutoProxyCreatorIfNecessary(parserContext.getRegistry(), parserContext.extractSource(sourceElement));
//必要时使用类代理
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
//注册组件
registerComponentIfNecessary(beanDefinition, parserContext);
}
先判断org.springframework.aop.config.internalAutoProxyCreator是否创建,进入else代码块,注册切面代理bean

先调用registerAutoProxyCreatorIfNecessary()方法,创建代理bean(第三个bean)

回到上层,再调用registerComponentIfNecessary()方法,将标签注册到代理的bean中
private static void registerComponentIfNecessary(@Nullable BeanDefinition beanDefinition, ParserContext parserContext) {
if (beanDefinition != null) {
parserContext.registerComponent(new BeanComponentDefinition(beanDefinition, "org.springframework.aop.config.internalAutoProxyCreator"));
}
}
回到ConfigBeanDefinitionParser.parse()方法,调用DomUtils.getChildElements()方法,解析标签的内容,得到所有子元素,循环遍历,获取每个元素的localName,进入对应的代码块,这里进入parseAspect()方法

解析标签,切面类的属性,循环标签的所有子标签

调用parseAdvice()方法,解析AOP的通知以及指向bean,得到RootBeanDefinition根级别的 Bean 定义,调用registerWithGeneratedName()方法,生成名称寄存器

public String registerWithGeneratedName(BeanDefinition beanDefinition) {
//生成唯一bean名称
String generatedName = this.generateBeanName(beanDefinition);
//注册bean
this.getRegistry().registerBeanDefinition(generatedName, beanDefinition);
return generatedName;
}
生成的bean名称是org.springframework.aop.aspectj.AspectJPointcutAdvisor,进入else代码块生成,调用uniqueBeanName()方法,名称改为org.springframework.aop.aspectj.AspectJPointcutAdvisor#0

回到上层,调用registerBeanDefinition()方法注册bean(这里不再详细讲解,参考前面示例),回到最外层XmlBeanDefinitionReader.doLoadBeanDefinitions()方法,计算出bean的总数4
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
try {
Document doc = this.doLoadDocument(inputSource, resource);
int count = this.registerBeanDefinitions(doc, resource);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
//省略部分代码... ...
}
打印第二行日志

回到AbstractApplicationContext.refresh()方法,往下走调用prepareBeanFactory()方法
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
try {
this.postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (BeansException var10) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);
}
this.destroyBeans();
this.cancelRefresh(var10);
throw var10;
} finally {
this.resetCommonCaches();
contextRefresh.end();
}
}
}
将生成好的bean工厂传入prepareBeanFactory()方法,注册所需依赖接口

至此基本容器配置初始化已经完成。
在 Spring 框架中,Bean 后置处理器(BeanPostProcessor)是一种特殊类型的Bean,它允许在容器实例化和配置其他所有Bean之前以及之后对Bean进行自定义处理。
回到refresh()方法,往下调用invokeBeanFactoryPostProcessors()方法
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
//里面没做什么处理,不讲解
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, this.getBeanFactoryPostProcessors());
if (!NativeDetector.inNativeImage() && beanFactory.getTempClassLoader() == null && beanFactory.containsBean("loadTimeWeaver")) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}
回到refresh()方法,往下调用registerBeanPostProcessors()方法
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}
进入registerBeanPostProcessors()方法, 注册Bean后置处理器,先调用getBeanNamesForType()方法,获取需要处理的名称

通过循环后置处理器,调用while(){}代码中的getBean()方法,将name转换为bean

最终进入到AbstractBeanFactory.doGetBean()方法,判断bean是单例,进入if判断,调用getSingleton()方法,获取单例bean实例

singletonObjects等于null,进入第二个if代码块,打印第三行日志,创建第一bean

详细的流程,如图

然后进入createBean()方法,调用doCreateBean()方法创建bean,进入第二if代码块,创建bean的包装实例,然后从实例中获取bean对象返回

自此注册Bean后置处理器结束
initMessageSource()是Spring框架中的一个方法,用于初始化消息源(MessageSource)。消息源是Spring国际化(i18n)功能的核心组件之一,它用于加载和解析不同语言的消息资源文件,以便应用程序可以根据不同地区和语言提供本地化的文本消息。
回到refresh()方法,往下调用initMessageSource()方法,注册beanName为messageSource的单例Bean(不做详细讲解)

在 Spring 框架中,应用程序事件多播器用于发送和接收应用程序中的事件,实现了观察者模式。
回到refresh()方法,往下调用initApplicationEventMulticaster()方法,注册beanName为applicationEventMulticaster的单例Bean(不做详细讲解)

registerListeners() 是 AbstractApplicationContext 类中的一个方法,用于注册监听器(Listener)到应用程序上下文(ApplicationContext)。通过调用 registerListeners() 方法,我们可以将自定义的监听器添加到应用程序上下文中,以便监听和响应相应的事件。
回到refresh()方法,往下调用registerListeners()方法(不做详细讲解)

当应用程序上下文刷新完成后,Spring 容器会调用 finishBeanFactoryInitialization() 方法来完成 Bean 工厂的初始化过程。这个方法会遍历应用程序上下文中所有的 Bean 定义,并根据定义的配置信息创建相应的 Bean 实例。
回到refresh()方法,往下调用finishBeanFactoryInitialization()方法,然后调用preInstantiateSingletons()方法,用于提前实例化所有单例 bean

进入到preInstantiateSingletons()方法,实例化单例,先将所有的beanName转换为一个List

然后进入嵌套循环,获取第一个beanName为test,获取根级别的 Bean 定义,判断bean定义不是抽象类,结束循环,调用getBean()方法获取,创建的bean对象

进入到getBean()方法,会发现实际上也是调用了doGetBean()方法(在注册Bean后置处理器中有讲解到),演示打印的日志过程
public Object getBean(String name) throws BeansException {
return this.doGetBean(name, (Class)null, (Object[])null, false);
}
调用getSingleton()方法,获取单例bean实例

进入getSingleton()方法,打印第四行日志

获取singletonObject对象时,进行第三bean的实例org.springframework.aop.aspectj.AspectJPointcutAdvisor#0(通常是因为在bean之间存在依赖关系)

然后按照原逻辑,打印第五行日志

然后进入createBean()方法,调用doCreateBean()方法创建bean
getSingleton()方法和createBean()方法的调用顺序并不是单纯的先后顺序,并且可能因为应用程序的复杂性而有所变化。这个现象并不影响Spring容器正常的初始化和运行流程

当beanName是org.springframework.aop.aspectj.AspectJPointcutAdvisor#0时,会先解析切面的表达式,进行一系列处理(这里不详细介绍)

当beanName是test时,由于配置了init-method属性,进入doCreateBean()方法,调用initializeBean()方法中的invokeInitMethods()方法,示例代码如下:

进入invokeInitMethods()方法,获取初始化方法名,进入if代码块,调用invokeCustomInitMethod()方法,找到类对应的方法名

然后调用invoke(bean)方法(动态代理),进入指定初始化方法,打印第6行日志。

test初始化完成之后,进行myAspect的初始化流程,打印第7行日志

finishRefresh()是Spring框架中的一个核心方法,它在Spring容器刷新时被调用,并负责将容器中的所有Bean初始化完成。该方法是Spring中整个容器初始化过程的最后一步。其作用是确保所有Bean的初始化工作都已经完成。
protected void finishRefresh() {
//清除资源缓存:为了提高性能和效率,会对一些常用的资源进行缓存
this.clearResourceCaches();
//初始化生命周期处理器
this.initLifecycleProcessor();
//刷新生命周期处理器,确保应用程序处于正确的生命周期状态。
this.getLifecycleProcessor().onRefresh();
//发布一个ContextRefreshedEvent事件,以通知已注册的事件监听器执行相应的操作。
this.publishEvent((ApplicationEvent)(new ContextRefreshedEvent(this)));
if (!NativeDetector.inNativeImage()) {
//将当前的应用程序上下文注册到Spring Actuator的LiveBeansView中,以便在运行时查看应用程序中的Bean信息。
LiveBeansView.registerApplicationContext(this);
}
}
所有准备工作完成后,调用getBean()方法,获取BeanName为test的实例,调用a()方法,在此之前先进入aop,声明了切入点

所以先进入before()方法,打印第8行日志

然后在进入自定义方法,打印第9行日志

然后调用applicationContext.close()方法关闭容器,打印上下文关闭时间,再进入自定义销毁方法

整个bean的初始化过程结束。
我们先讲解Bean初始化的顺序优先级:构造方法、注解、实现类、指定初始化方法,示例代码如下:
public class Test implements InitializingBean {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Test bean = applicationContext.getBean(Test.class);
/** Output:
* 构造方法初始化
* 注解初始化
* 实现类初始化
* 指定初始化方法
*/
}
public Test() {
System.out.println("构造方法初始化");
}
public void init(){
System.out.println("指定初始化方法");
}
@PostConstruct
public void init2(){
System.out.println("注解初始化");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("实现类初始化");
}
}
由此可以得出初始化优先级:构造方法>注解>实现类>指定初始化。
然后再来讲解Bean销毁的顺序优先级:注解、实现类、指定初始化方法,示例代码如下:
public class Test implements DisposableBean {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Test bean = applicationContext.getBean(Test.class);
applicationContext.close();
/** Output:
* 注解销毁
* 实现类销毁
* 指定销毁方法
*/
}
public void destroy1(){
System.out.println("指定销毁方法");
}
@PreDestroy
public void destroy2(){
System.out.println("注解销毁");
}
@Override
public void destroy() throws Exception {
System.out.println("实现类销毁");
}
}
由此可以得出销毁优先级:注解>实现类>指定初始化。
在之前的介绍中我们知道容器常用的启动有两种:AnnotationConfigApplicationContext和ClassPathXmlApplicationContext。
经过本章节的介绍相信对ClassPathXmlApplicationContext的流程已经不陌生了,下面简单介绍AnnotationConfigApplicationContext的启动流程:
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.example");
当我们定义了包名访问路径,再其构造方法中,会调用scan(basePackages)方法,扫描包路径下的所有类,然后再去调用公用的refresh()方法。
public AnnotationConfigApplicationContext(String... basePackages) {
this();
this.scan(basePackages);
this.refresh();
}
再doScan()方法中,将扫描的类转换成BeanDefinition,示例代码如下:

总体来说,两种方式启动的作用是相同的,都是启动Spring容器并管理Bean。只是配置的来源不同,一个是基于Java配置类,一个是基于XML配置文件。

一级缓存是 Spring 默认开启的缓存,它是基于 Spring 的 BeanFactory 实现的。当我们在应用程序中获取一个 bean 时,Spring 会首先查找一级缓存,如果在缓存中找到该 bean,则直接返回缓存中的实例,否则创建新的实例,并将其放入缓存中。
一级缓存是基于 Spring 容器的,因此对于每个容器,都有它自己的一级缓存。当容器关闭时,所有的一级缓存也会被销毁。
二级缓存是在 Spring 中为了加快 bean 的创建而引入的。它是基于 ApplicationContext 实现的,可以被多个容器共享。当多个容器同时需要同一个 bean 时,二级缓存可以避免重复创建实例,提高应用程序的性能。
Spring 的二级缓存默认是关闭的,需要手动开启。可以通过在 Spring 配置文件中添加 或者在 Java Config 中使用 @EnableCaching 注解来开启缓存功能。
三级缓存是指在 Spring AOP 中使用的缓存机制。当使用 Spring AOP 进行方法拦截时,Spring 会将拦截后的结果存储在三级缓存中。如果下次使用同样的参数调用相同的方法,则 Spring 会直接从缓存中获取结果,避免重复执行拦截逻辑。
需要注意的是,三级缓存仅适用于基于代理的 AOP,不适用于基于 AspectJ 的 AOP。
对象在三级缓存中的创建流程:
A依赖B,B依赖A
1、A创建过程中需要B,于是先将A放到三级缓存,去实例化B。
2、B实例化的过程中发现需要A,于是B先查一级缓存寻找A,如果没有,再查二级缓存,如果还没有,再查三级缓存,找到了A,然后把三级缓存里面的这个A放到二级缓存里面,并删除三级缓存里面的A。
3、B顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中的状态)。然后回来接着创建A,此时B已经创建结束,可以直接从一级缓存里面拿到B,去完成A的创建,并将A放到一级缓存。
只有单例的bean会通过三级缓存提前暴露来解决循环依赖的问题,而非单例的bean,每次从容器中获取都是一个新的对象,都会重新创建,所以非单例的bean是没有缓存的,不会将其放到三级缓存中。
二三级缓存就是为了解决循环依赖,且之所以是二三级缓存而不是二级缓存,主要是可以解决循环依赖对象需要提前被aop代理(三级缓存,就是给AOP动态代理用的)。