Java进击框架:Spring-Bean初始化过程(五)
- 前言
- 源码
- 初始化配置
- 加载Bean
- 加载Bean对象
- 加载Bean切面
- Bean工厂后置处理器
- 注册Bean后置处理器
- 初始化消息源
- 初始化应用程序事件多播器
- 注册监听器
- 完成Bean工厂初始化
- Bean初始化完成刷新
- 应用上下文创建完成
- Bean初始化和销毁的不同顺序
- 启动容器的区别
前言
本章节主要介绍,面试中经常问到的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");}
}
<?xml version="1.0" encoding="UTF-8"?>
<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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttps://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
加载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,从根节点<beans>
元素开始,获取根节点的子节点列表,进入循环,第一次是空格,第二次返回一个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的逻辑进行。
循环下一个<aop:config>
标签,判断不是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()
方法,将<aop:config>
标签注册到代理的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()
方法,解析<aop:config>
标签的内容,得到所有子元素,循环遍历,获取每个元素的localName,进入对应的代码块,这里进入parseAspect()
方法
解析<aop:aspect>
标签,切面类的属性,循环<aop:aspect>
标签的所有子标签
调用parseAdvice()
方法,解析AOP的通知以及指向bean,得到RootBeanDefinition根级别的 Bean 定义,调用registerWithGeneratedName()
方法,生成名称寄存器
public String registerWithGeneratedName(BeanDefinition beanDefinition) {//生成唯一bean名称String generatedName = this.generateBeanName(beanDefinition);//注册beanthis.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()
方法,注册所需依赖接口
至此基本容器配置初始化已经完成。
Bean工厂后置处理器
在 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()));}}
注册Bean后置处理器
回到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()
方法(不做详细讲解)
完成Bean工厂初始化
当应用程序上下文刷新完成后,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行日志
Bean初始化完成刷新
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初始化和销毁的不同顺序
我们先讲解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("指定初始化方法");}@PostConstructpublic void init2(){System.out.println("注解初始化");}@Overridepublic 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("指定销毁方法");}@PreDestroypublic void destroy2(){System.out.println("注解销毁");}@Overridepublic void destroy() throws Exception {System.out.println("实现类销毁");}
}
由此可以得出销毁优先级:注解>实现类>指定初始化。
启动容器的区别
在之前的介绍中我们知道容器常用的启动有两种:AnnotationConfigApplicationContext和ClassPathXmlApplicationContext。
- AnnotationConfigApplicationContext:基于Java配置类的容器启动方式,通常用于基于注解的配置。
- ClassPathXmlApplicationContext基于XML配置文件的容器启动方式,通常用于基于XML的配置。
经过本章节的介绍相信对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配置文件。