Java进击框架:Spring-Bean初始化过程(五)
前言
本章节主要介绍,面试中经常问到的Spring-Bean初始化过程。
源码
接下来会通过以下代码,进行debug 调试,讲解(spring版本为5.3.23,源码太长会转换为图片,节省字数,提高阅读性)。
java
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
<?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/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>
初始化配置
首先进入构造方法,会设置默认值。
java
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()
方法
java
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()
方法
java
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
this.refreshBeanFactory();
return this.getBeanFactory();
}
先调用AbstractRefreshableApplicationContext.refreshBeanFactory()
方法,
java
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()
方法
java
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包、类路径啥的)
java
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()
方法
java
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
java
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()
方法,
java
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)
方法
java
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)
方法
java
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类(一个标签对应所有元素的类), 设置相对应的类路径。
java
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判断
java
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()
方法
java
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中
java
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()
方法,生成名称寄存器
java
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
java
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()
方法
java
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()
方法
java
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()
方法
java
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后置处理器中有讲解到),演示打印的日志过程
java
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的初始化工作都已经完成。
java
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初始化的顺序优先级:构造方法、注解、实现类、指定初始化方法,示例代码如下:
java
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销毁的顺序优先级:注解、实现类、指定初始化方法,示例代码如下:
java
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。
- AnnotationConfigApplicationContext :基于Java配置类的容器启动方式,通常用于基于注解的配置。
- ClassPathXmlApplicationContext 基于XML 配置文件的容器启动方式,通常用于基于XML的配置。
经过本章节的介绍相信对ClassPathXmlApplicationContext 的流程已经不陌生了,下面简单介绍AnnotationConfigApplicationContext的启动流程:
java
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.example");
当我们定义了包名访问路径,再其构造方法中,会调用scan(basePackages)
方法,扫描包路径下的所有类,然后再去调用公用的refresh()
方法。
java
public AnnotationConfigApplicationContext(String... basePackages) {
this();
this.scan(basePackages);
this.refresh();
}
再doScan()
方法中,将扫描的类转换成BeanDefinition ,示例代码如下:
总体来说,两种方式启动的作用是相同的,都是启动Spring 容器并管理Bean 。只是配置的来源不同,一个是基于Java 配置类,一个是基于XML配置文件。