Spring源码系列:容器的基本实现

30 篇文章 3 订阅

前言

什么是容器?

  1. Spring容器Application的一个实例对象。
  2. 容器负责的实例化、配置Bean、管理Bean的生命周期。
  3. Spring容器将我们代码中的PoJo类、XML配置文件转化为一个可用的系统。

那么接下来会以spring-beans包下的一些核心类展开来讲解容器是怎么实现的。

一. 容器的基本实现

要想了解Spring容器的概念和深入源码,相信一切都得从这个容器的出生开始,那么在此以DefaultListableBeanFactory为切入点来讲解。

那么DefaultListableBeanFactory类是干什么的呢?用百度翻译一下源码中的注释,如下:

  1. 作为ConfigurableListableBeanFactoryBeanDefinitionRegistry接口的默认实现。
  2. 一个基于bean定义元数据的工厂类,用于注册所有的bean。(可能是PoJo类、配置文件)。
  3. 可以操作预先解析的bean元数据对象。

那么来看下这个类的关系图:
在这里插入图片描述
我们可以重点关注图中蓝色框圈起来的部分,我们可以做个总结,DefaultListableBeanFactory类对bean的作用有两个方向:

  • 进行监听,对于满足条件的bean进行定义注册
  • bean进行增删改查操作(一些动作实现)

DefaultListableBeanFactory作为整个bean加载的核心部分,是Spring注册和加载bean的一个默认实现。其还有个子类XmlBeanFactory,主要用于从XML文件中读取BeanDefinition

我个人理解是这样的:

  • 一些PoJo类,其加载一般交给DefaultListableBeanFactory来执行。
  • 而对于XML形式配置的Bean,则交给XmlBeanFactory来执行。

Tip:

Definition是什么意思?其单词本意是:定义。
BeanDefinition像是对Bean的一个抽象模板,定义了Bean的一些行为、属性等。
那么自然而然的,BeanDefinitionRegistry就是该模板的注册器了。

1.1 资源的读取

Spring的大部分功能都是以配置作为切入点。而上文提到的,XmlBeanFactory负责XML配置形式的Bean的加载。而从XML这类资源文件读取、解析以及注册等流程,则交给XmlBeanDefinitionReader来完成。

来看下XmlBeanDefinitionReader的类关系图:
在这里插入图片描述
XmlBeanDefinitionReader类下有这么几个重要的成员:

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
	// 定义从资源文件加载到转化为Document的功能
	private DocumentLoader documentLoader = new DefaultDocumentLoader();
	// 前者负责转化为Document,那么documentReaderClass 就负责读取Document 并注册 BeanDefinition
	private Class<? extends BeanDefinitionDocumentReader> documentReaderClass = DefaultBeanDefinitionDocumentReader.class;
}

XmlBeanDefinitionReader类的父类AbstractBeanDefinitionReader下又有这么几个重要的成员:

public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader, EnvironmentCapable {
	// 定义资源加载器,主要应用于根据给定的资源文件地址,返回对应的Resource。
	private ResourceLoader resourceLoader;
}

将上文做个总结,XmlBeanDefinitionReader主要做的事情就是:

  1. 利用父类AbstractBeanDefinitionReaderResourceLoader将资源文件路径转化为对应的Resource
  2. Resource进行文件转换,转换为Document文件。
  3. 使用DefaultBeanDefinitionDocumentReader进行文件解析。

1.1.1 Resource资源

Spring的配置文件是通过ClassPathResource来封装的,我们来看下他的类关系图:
在这里插入图片描述
顶层接口InputStreamSource只提供了一个方法:提供返回InputStream流的方法。

public interface InputStreamSource {
	InputStream getInputStream() throws IOException;
}

Resource接口用于封装底层资源,抽象了所有Spring内部使用到的资源:FlieURLClasspath等。提供了3个判断当前资源状态的方法。

  1. 存在性exists()
  2. 可读性isReadable()
  3. 是否处于打开状态isOpen()

同时还提供了不同资源到URLURIFile类型的转换,Resource接口的具体实现有:

  1. FileSystemResource(文件)。
  2. ClassPathResourceClassPath资源)。
  3. UrlResourceURL资源)。
  4. InputStreamSourceInputStream资源)。
  5. ByteArrayResourceByte数组)。

1.2 资源的加载和解析

在Spring将配置文件封装为Resource类型的实例后,就会由XmlBeanDefinitionReader来完成资源加载。
我们来直接看其核心方法loadBeanDefinitions(),上文读取好的资源文件(Resource实例)则作为其参数传入:

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
	@Override
	public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(new EncodedResource(resource));
	}
}

直观的来看这个方法,我们发现,在做Bean加载之前,会对Resource实例对象进行编码。

// 先对Resource资源进行编码封装
new EncodedResource(resource)

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
	Assert.notNull(encodedResource, "EncodedResource must not be null");
	if (logger.isTraceEnabled()) {
		logger.trace("Loading XML bean definitions from " + encodedResource);
	}
	// 用来记录 已经加载完成的资源 的Set集合
	Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
	// 1.如果发现,该资源已经被加载过,那么抛异常,说明你这个资源重复加载了。
	if (!currentResources.add(encodedResource)) {
		throw new BeanDefinitionStoreException(
				"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
	}
	// 2.获取每个Resource资源对应的inputStream流
	try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
		InputSource inputSource = new InputSource(inputStream);
		// 2.1 设置对应的编码,这是考虑到Resource可能存在编码要求的情况
		if (encodedResource.getEncoding() != null) {
			inputSource.setEncoding(encodedResource.getEncoding());
		}
		// 2.2 进行真正的Bean加载
		return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
	}
	// ...
}

再看下核心的doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法:

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {

	try {
		Document doc = doLoadDocument(inputSource, resource);
		int count = registerBeanDefinitions(doc, resource);
		if (logger.isDebugEnabled()) {
			logger.debug("Loaded " + count + " bean definitions from " + resource);
		}
		return count;
	}
	// 以下都是各种catch方法,我们主要关注try语句块中做的事情即可。
}

这个方法主要做三件事情:

  1. 获取XML文件的验证模式。
  2. 加载XML文件,并得到对应的Document
  3. 根据返回的Document注册Bean信息。

doLoadDocument()这个方法,则做了前两件事情。我们以这行代码为切入点,来展开。

protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
	// 这里的documentLoader指的是上文提到的DefaultDocumentLoader,负责将Resource实例转化为Document
	return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
			getValidationModeForResource(resource), isNamespaceAware());
}

1.2.1 获取XML的验证模式

XML的验证模式有什么用?其保证了XML文件的正确性。常用的验证模式有两种:

DTD

Document Type Definition:文档类型定义,一种XML约束模式语言,是XML文件的验证机制。 属于XML文件组成的一部分。可以通过比较XML文档和DTD文件来判断文档是否符合规范。一个DTD文档包含:

  1. 元素的定义规则。
  2. 元素间关系的定义规则。
  3. 元素可使用的属性。
  4. 可使用的实体或者符号规则。

DTD案例:注意DOCTYPE

<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN"  
"http://www.springframework.org/dtd/spring-beans-2.0.dtd">  
<beans>  
  
</beans>  
XSD

XML Schemas Definition:XML Schema语言就是XSD。描述了XML文档的结构。XML Schema 本身就是XML文档,符合其语法结构,可以用通用的XML解析器来解析。一个XSD包括:

  1. 文档中出现的元素。
  2. 文档中出现的属性、子元素。
  3. 子元素的数量和顺序。
  4. 元素是否为空。
  5. 元素和属性的数据类型。
  6. 元素或属性的默认和固定值。

XSD案例:

<?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"  
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"  
    xsi:schemaLocation="  
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd  
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd  
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">  
  
</beans>  

使用XML文档的时候,必须做到几点:

  1. 声明名称空间xmlns="http://www.springframework.org/schema/beans"
  2. 指定该名称空间对应的XML Schema文档的存储位置xsi:schemaLocation="xxx"一部分是名称空间的URI,另一部分是该名称空间所标识的XML Schema文件位置或者URL地址。

其他:

  1. 声明XML Schema 实例xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

言归正传,我们回到代码本身,来关注下getValidationModeForResource()方法:

protected int getValidationModeForResource(Resource resource) {
	int validationModeToUse = getValidationMode();
	// 如果手动制定了验证模式,则使用指定的验证模式
	if (validationModeToUse != VALIDATION_AUTO) {
		return validationModeToUse;
	}
	// 如果没有手动指定,那么使用自动检测
	int detectedMode = detectValidationMode(resource);
	if (detectedMode != VALIDATION_AUTO) {
		return detectedMode;
	}
	return VALIDATION_XSD;
}

其实detectValidationMode()方法并不是很难理解,我只会贴出最最核心的代码:

private static final String DOCTYPE = "DOCTYPE";

if (hasDoctype(content)) {
	isDtdValidated = true;
	break;
}

private boolean hasDoctype(String content) {
	return content.contains(DOCTYPE);
}

说白了就是,如果发现文档中包含了DOCTYPE,该XML模式就是DTD,否则就是XSD。(看到这里可以回顾下上文的DTD案例)

1.2.2 获取Document

在验证完XML模式的合法性后,会将Resource实例转化为Document,再来回顾这行代码:

protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
	// 这里的documentLoader指的是上文提到的DefaultDocumentLoader,负责将Resource实例转化为Document
	return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
			getValidationModeForResource(resource), isNamespaceAware());
}

其本质是通过SAX解析XML文档。

SAX解析:逐行扫描文档,一边扫描一边解析。其工作原理简单地说就是:
对文档进行顺序扫描,当扫描到文档(document)开始与结束、元素(element)开始与结束、文档(document)结束等地方时通知事件处理函数,由事件处理函数做相应动作,然后继续同样的扫描,直至文档结束。

我们先来看下getEntityResolver()这个方法是干什么的:

protected EntityResolver getEntityResolver() {
	if (this.entityResolver == null) {
		// Determine default EntityResolver to use.
		ResourceLoader resourceLoader = getResourceLoader();
		if (resourceLoader != null) {
			this.entityResolver = new ResourceEntityResolver(resourceLoader);
		}
		else {
			this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader());
		}
	}
	return this.entityResolver;
}
EntityResolver

什么是EntityResolver

如果SAX应用程序需要实现自定义的处理外部实体,则必须实现此接口并使用setEntityResolver方法向SAX驱动器注册一个实例。

而对于项目本身而言,则可以提供一个寻找DTD声明的方法。
我们来看下EntityResolver接口:

public interface EntityResolver {
    public abstract InputSource resolveEntity (String publicId, String systemId) throws SAXException, IOException;
}

他接收俩参数,publiIdsystemId
以上文的DTDXSD案例为例,若是读取XSD配置文件,则获得的参数如下:

  • publiIdnull
  • systemIdhttp://www.springframework.org/schema/beans/spring-beans-3.0.xsd

若读取的是DTD文件:

  • publiId-//SPRING//DTD BEAN 2.0//EN
  • systemIdhttp://www.springframework.org/dtd/spring-beans-2.0.dtd

为啥会出现不同呢?Spring使用DelegatingEntityResolver来实现该接口:

public class DelegatingEntityResolver implements EntityResolver {
	public static final String DTD_SUFFIX = ".dtd";
	public static final String XSD_SUFFIX = ".xsd";
	@Override
	@Nullable
	public InputSource resolveEntity(@Nullable String publicId, @Nullable String systemId) throws SAXException, IOException {
		if (systemId != null) {
			// 若加载dtd类型,则直接截取systemId最后的xx.dtd,然后去当前路径下寻找
			if (systemId.endsWith(DTD_SUFFIX)) {
				return this.dtdResolver.resolveEntity(publicId, systemId);
			}
			// 若加载xsd类型,则默认到META-INF/Spring.schemas文件中找到systemId对应的XSD文件并加载。
			else if (systemId.endsWith(XSD_SUFFIX)) {
				return this.schemaResolver.resolveEntity(publicId, systemId);
			}
		}
		return null;
	}
}

最后,关于如何转换Document,即loadDocument()方法的最终实现就简单概括,其由DefaultDocumentLoader来完成。

public class DefaultDocumentLoader implements DocumentLoader {
	@Override
	public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
			ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {

		DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
		if (logger.isTraceEnabled()) {
			logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
		}
		DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
		return builder.parse(inputSource);
	}
}

主要做三件事:

  1. 创建DocumentBuilderFactory工厂。
  2. 工厂创建一个文档构造器DocumentBuilder
  3. 解析inputSource来生成Document对象。

1.2.3 解析和注册BeanDefinition

上文的代码里,只剩下这行代码没有讲解了,也就是在将文件转化为Document后,重点做的事情:提取和注册Bean

// doc则是1.2.2中获取到的Document
int count = registerBeanDefinitions(doc, resource);

代码展开:

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		// 1.实例化BeanDefinitionDocumentReader 
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		// 2.获取之前已经加载好的BeanDefinition个数
		int countBefore = getRegistry().getBeanDefinitionCount();
		// 3.加载和注册Bean
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		// 4.记录本次加载的BeanDefinition个数
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}
}

BeanDefinitionDocumentReader只是一个接口,应用单一职责的原则,将具体的逻辑registerBeanDefinitions()方法委托给单一的类去进行处理。具体的实现类为DefaultBeanDefinitionDocumentReader,我们来看下其具体的实现:

public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
	@Override
	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;
		doRegisterBeanDefinitions(doc.getDocumentElement());
	}
	
	protected void doRegisterBeanDefinitions(Element root) {
		BeanDefinitionParserDelegate parent = this.delegate;
		this.delegate = createDelegate(getReaderContext(), root, parent);

		if (this.delegate.isDefaultNamespace(root)) {
			// 处理profile属性
			String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
			if (StringUtils.hasText(profileSpec)) {
				String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
						profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
				if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
					if (logger.isDebugEnabled()) {
						log..
					}
					return;
				}
			}
		}
		// 解析前处理,交给子类实现。父类给子类提供模板,即模板模式的一个体现
		preProcessXml(root);
		parseBeanDefinitions(root, this.delegate);
		// 解析后处理,交给子类实现
		postProcessXml(root);

		this.delegate = parent;
	}
}

profile属性,用于在配置文件中指定开发环境,这样可以方便的进行切换开发、部署环境。常用的是更换不同的数据库。
如同一个配置文件中:

<bean profile="dev">xx</bean>
<bean profile="production">xx</bean>

那么集成到Web环境中,则在web.xml中加入以下代码:

<context-param>
	<param-name>Spring.profiles.active</param-name>
	<param-value>dev</param-value>
</context-param>

接下来再看看parseBeanDefinitions方法:

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
	// bean处理
	if (delegate.isDefaultNamespace(root)) {
		NodeList nl = root.getChildNodes();
		for (int i = 0; i < nl.getLength(); i++) {
			Node node = nl.item(i);
			if (node instanceof Element ele) {
				// bean处理
				if (delegate.isDefaultNamespace(ele)) {
					parseDefaultElement(ele, delegate);
				}else {
					// bean处理
					delegate.parseCustomElement(ele);
				}
			}
		}
	}
	else {
		delegate.parseCustomElement(root);
	}
}

Spring的XML配置中Bean的声明方式有两大类:

  • 默认的:<bean id = "xx" class="xx.xx"/>
  • 自定义的:<tx:annotation-driven/>

上述代码也就是对不同情况进行不同的Bean处理。其中核心的parseDefaultElementparseCustomElement方法则在下文继续讲解。

1.3 总结

在这里,先对上文做个总结,方便大家思考和理解。

DefaultListableBeanFactory作为Bean加载的一个核心部分,是Spring注册和加载Bean的一个默认实现。有两个重要的功能:

  • 注册和加载Bean。(顶层实现AliasRegistry
  • Bean进行增删改查等操作。(顶层实现BeanFactory)

Spring的大部分功能都是以配置作为切入点。XML这类资源文件的读取、解析注册都是在XmlBeanDefinitionReader类中来完成。

资源读取:

  • Spring有自己的资源接口Resource,用于将不同类型的资源对象抽象成Resource实例对象。

资源解析:

  • XmlBeanDefinitionReaderloadBeanDefinitions()方法进行Bean的加载解析。
  • Resource实例对象进行编码。
  • 获取XML文件的验证模式(共两种:DTDXSD)。
  • 若验证通过,加载XML文件(使用Sax解析,即一边扫描XML一边解析),将Resource实例对象中的InputStream流转化为对应的Document对象doc

资源注册:

  • 根据doc来提取和注册Bean
  • 通过DefaultBeanDefinitionDocumentReaderregisterBeanDefinitions()方法先处理profile属性(用于在配置文件中指定开发环境)。
  • 然后再解析标签处理生成BeanDefinition

到这里Spring的Bean容器(工厂)对资源的处理工作也就做完了,更深层次的,对于Bean层面的解析和加载则交给后文。

二. 简单的小案例

我是在Spring5.0.x版本源码项目上,创建了自己的Test,如图:
在这里插入图片描述
创建User类:

public class User {
	private int id;
	private String name;
	// get set
}

test目录下的resources资源文件目录中,创建user.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"
	   xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
	   xsi:schemaLocation="
   	http://www.springframework.org/schema/beans
   	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
   	http://www.springframework.org/schema/aop
   	http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
   	http://www.springframework.org/schema/tx
   	http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
   	http://www.springframework.org/schema/context
   	http://www.springframework.org/schema/context/spring-context-3.0.xsd">
	<bean name="user" class="org.springframework.beans.User">
		<property name="id" value="1" />
		<property name="name" value="你好" />
	</bean>
</beans>

测试类:

package org.springframework.beans;

import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;

public class Test {
	@org.junit.jupiter.api.Test
	public void test() {
		ClassPathResource resource = new ClassPathResource("user.xml");
		XmlBeanFactory factory = new XmlBeanFactory(resource);
		User user = (User) factory.getBean("user");
		System.out.println(user.getId());
		System.out.println(user.getName());
	}
}

结果如下:
在这里插入图片描述
一般读取XML形式的Bean,有三步:

  1. 通过ClassPathResource加载对应的xml文件。
  2. 通过resource实例对象创建出Bean工厂XmlBeanFactory
  3. Bean工厂通过Name来获取对应的Bean

备注:注意,XmlBeanFactory对于Spring来说,已经是个过时的类了。不推荐使用。上述代码可以改为(本质一样的):

@org.junit.jupiter.api.Test
public void test() {
	BeanFactory factory = new DefaultListableBeanFactory();
	XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader((BeanDefinitionRegistry) factory);
	
	ClassPathResource resource = new ClassPathResource("user.xml");
	reader.loadBeanDefinitions(resource);
	
	User user = (User) factory.getBean("user");
	System.out.println(user.getId());
	System.out.println(user.getName());
}

本篇文章,从外层看,已经介绍了Bean容器对资源的一个加载和解析流程,而上述的案例中,Spring是如何把XML配置中的Bean加载进来的?又是如何得到我们配置的字段值的?答案也就是源码中核心的parseDefaultElementparseCustomElement方法。下篇文章则从标签的解析来做具体的展开介绍。

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
好的,我会尽力解释 Spring 容器的启动流程源码。 1. 加载配置文件 Spring 容器启动时,首先会读取配置文件,通常是 `applicationContext.xml`。配置文件中定义了需要被 Spring 管理的 bean,以及它们的依赖关系等信息。 2. 创建 BeanDefinition Spring 容器会解析配置文件,创建对应的 BeanDefinition 对象。BeanDefinition 定义了 bean 的基本信息,如 bean 的类名、scope、依赖关系等等。 3. 注册 BeanDefinition 接下来,Spring 容器将创建的 BeanDefinition 注册到 BeanDefinitionRegistry 中。BeanDefinitionRegistry 是一个接口,它定义了注册、查询、删除 BeanDefinition 的方法。 4. 实例化 Bean 接下来,Spring 容器将开始实例化 bean。Spring 容器使用反射创建 bean 的实例,然后根据配置文件中的信息对 bean 进行初始化。 5. 填充属性值 在 bean 实例化之后,Spring 容器会开始填充属性值。Spring 容器会根据配置文件中的信息,自动为 bean 填充属性值。这些属性可以是基本类型、引用类型、集合类型等等。 6. 调用 BeanPostProcessor 在填充完属性值之后,Spring 容器会调用 BeanPostProcessor 的方法。BeanPostProcessor 是一个接口,它定义了在 bean 实例化和初始化过程中的回调方法。通过实现 BeanPostProcessor,我们可以在 bean 实例化和初始化的过程中做一些自定义的操作。 7. 初始化 Bean 在填充完属性值和调用 BeanPostProcessor 之后,Spring 容器会调用 bean 的初始化方法。初始化方法可以是 init-method 指定的方法,也可以是实现了 InitializingBean 接口的 afterPropertiesSet() 方法。 8. 注册销毁方法 当 bean 的生命周期结束时,Spring 容器会调用它的销毁方法。销毁方法可以是 destroy-method 指定的方法,也可以是实现了 DisposableBean 接口的 destroy() 方法。 以上就是 Spring 容器启动流程的大概过程。其中,BeanDefinition、BeanPostProcessor、InitializingBean、DisposableBean 等接口和类都是 Spring 框架中提供的,它们为我们提供了更加灵活的配置和扩展方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zong_0915

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值