基于XML的IOC容器初始化以及循环依赖问题——Spring中的refresh()方法
1,找到入口
我们首先找到测试的入口来一探究竟,相信下面这段代码大家都写过:
@Test
public void test01() {
// 入口
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
TestService bean = applicationContext.getBean(TestService.class);
bean.test();
}
我们先来看一下 ClassPathXmlApplicationContext的所处的体系:
ApplicationContext 允许上下文嵌套,通过保持父上下文可以维持一个上下文体系。对于 Bean 的查找可以在这个上下文体系中发生,首先检查当前上下文,其次是父上下文,逐级向上,这样为不同的 Spring 应用提供了一个共享的 Bean 定义环境。
首先根据上面的代码点进来:
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
其实际调用的构造函数为:
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);// 位置1
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
实际上,xml应用中正是通过这个构造方法进行的IOC初始化过程。
2,准备工作
通过分析ClassPathXmlApplicationContext的源代码可以知道,在创建ClassPathXmlApplicationContext容器时,构造方法首先做了以下两项工作
2.1. 配置Bean资源加载器
从上面代码的位置1看进去,结合类图看一下以下代码,可以发现其层层调用父类的构造方法,直到AbstractApplicationContext。
// AbstractXmlApplicationContext中
public AbstractXmlApplicationContext(@Nullable ApplicationContext parent) {
super(parent);
}
// AbstractRefreshableConfigApplicationContext中
public AbstractRefreshableConfigApplicationContext(@Nullable ApplicationContext parent) {
super(parent);
}
// AbstractRefreshableApplicationContext
public AbstractRefreshableApplicationContext(@Nullable ApplicationContext parent) {
super(parent);
}
// AbstractApplicationContext
public AbstractApplicationContext(@Nullable ApplicationContext parent) {
this(); // 位置1
setParent(parent);
}
接下来我们点进去上面位置1的代码,如下所示
public AbstractApplicationContext() {
this.resourcePatternResolver = getResourcePatternResolver();
}
// 设置资源加载器
protected ResourcePatternResolver getResourcePatternResolver() {
return new PathMatchingResourcePatternResolver(this);//位置1
}
// AbstractApplicationContext这里其实也是一个资源加载器
public abstract class AbstractApplicationContext extends DefaultResourceLoader ....
这里可以看到,位置1处, AbstractApplicationContext将自己设置为了Bean资源加载器并返回。
因此ClassPathXmlApplicationContext中super(parent);这行代码的目的就是为容器设置好Bean资源加载器(AbstractApplicationContext)。
2.2 设置配置信息
有了加载器,那么肯定就还需要加载器所加载的配置文件信息,setConfigLocations(configLocations);这行代码,目的就是对Bean配置信息进行定位。
// AbstractRefreshableConfigApplicationContext中的方法
public void setConfigLocations(@Nullable String... locations) {
if (locations != null) {
Assert.noNullElements(locations, "Config locations must not be null");
this.configLocations = new String[locations.length];
for (int i = 0; i < locations.length; i++) {
this.configLocations[i] = resolvePath(locations[i]).trim();
}
}
else {
this.configLocations = null;
}
}
通过以上代码我们也可以看出,我们即可以使用一个字符串来配置多个Spring Bean配置信息,也可以使用字符串数组,即下面两种方式都可以:
ClassPathResource res = new ClassPathResource("a.xml,b.xml");
ClassPathResource res =new ClassPathResource(new String[]{"a.xml","b.xml"});
3,开始启动
SpringIOC 容器对 Bean 配置资源的载入是从 refresh()函数开始的,ClassPathXmlApplicationContext 通过调用其父类 AbstractApplicationContext 的 refresh()函数启动整个 IOC 容器对 Bean 定义的载入过程,现在我们来详细看看 refresh()中的逻辑处理:
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// 调用容器准备刷新的方法,获取容器的当时时间,同时给容器设置同步标识
prepareRefresh();
// 载入 位置1
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 为 BeanFactory 配置容器特性,例如类加载器、事件处理器等
prepareBeanFactory(beanFactory);
...
}
}
我们说的载入过程,其实就是从obtainFreshBeanFactory();(位置1)这个地方开始的。
refresh()方法的主要作用是:在创建 IOC 容器前,如果已经有容器存在,则需要把已有的容器销毁和 关闭,以保证在 refresh 之后使用的是新建立起来的 IOC 容器。它类似于对 IOC 容器的重启,在新建立 好的容器中对容器进行初始化,对 Bean 配置资源进行载入。
4,载入
obtainFreshBeanFactory()方法调用子类容器【AbstractApplicationContext】的 refreshBeanFactory()方法,启动容器载入 Bean 配置信息的过程,代码如下:
// AbstractApplicationContext
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
return getBeanFactory();
}
// 抽象方法
protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;
AbstractApplicationContext 类中只抽象定义了 refreshBeanFactory()方法,容器真正调用的是其子类 AbstractRefreshableApplicationContext 实现的 refreshBeanFactory()方法,方法的源码如下:
@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) { // 位置1
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();// 位置2
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);// 位置3
this.beanFactory = beanFactory;
}
catch (IOException ex) { ... }
}
在这个方法中,首先判断BeanFactory是否存在,如果存在则先销毁beans并关闭beanFactory(位置1),接着创建DefaultListableBeanFactory(位置2),并调用loadBeanDefinitions(beanFactory)方法装载bean定义(位置3)。重点是位置3的装载bean定义的调用。
5,配置资源读取器
我们从4步骤中的位置3点进去:
// AbstractRefreshableApplicationContext 抽象方法
protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
throws BeansException, IOException;
// AbstractXmlApplicationContext具体实现
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 创建一个XmlBeanDefinitionReader读取Bean配置资源
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 位置1
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
该步骤的目的主要是使用我们定义的XML资源读取器(XmlBeanDefinitionReader)去加载配置资源。
6,处理文件并加载
从上步骤代码的位置1点进去,最终我们可以看到如下代码:
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException("...");
}
if (resourceLoader instanceof ResourcePatternResolver) {
try {
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);// 位置1
int count = loadBeanDefinitions(resources); // 位置2
...
}
}
在该处我们可以看到程序就做了两件事:
-
位置1:调用资源加载器的获取资源方法 resourceLoader.getResource(location),获取到要加载的资源
其实就是 XmlBeanDefinitionReader 通过调用ClassPathXmlApplicationContext的父类DefaultResourceLoader的getResource()方法获取要加载的资源。
-
位置2:加载bean定义
在下个步骤中详细解释
总结一下这个流程的目的:寻找要加载资源的位置并加载。
7,读取配置内容
继续上个环节的位置2处的代码:loadBeanDefinitions(resources)。
该处的主要目的是加载Bean定义,通过点进去几层,我们最终来到了XmlBeanDefinitionsReader的这个位置:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
Document doc = doLoadDocument(inputSource, resource);// 位置1
int count = registerBeanDefinitions(doc, resource);// 位置2
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
catch (BeanDefinitionStoreException ex) {...}
}
这一步的目的是:将配置文件解析为文档对象并注册Bean定义。
-
解析文档对象
从位置1看进去,最终会来到 DefaultDocumentLoader 的 loadDocument() 方法:
@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); }
通过这个操作,Spring就完成了将配置文件转换成为了Document对象的过程。
-
注册Bean定义
接下来的步骤将会解释如何将Document对象解析为SpringIOC容器管理的Bean对象,并注册到容器中的。
8,创建 BeanDefinitionDocumentReader 解析资源
XmlBeanDefinitionReader 类中的 doLoadBeanDIfinition()(上步骤的)方法是从特定的XML文件中实际载入Bean配置资源的方法,该方法在载入Bean配置资源之后将其转换为Document对象,接下来调用registerBeanDefinitions() 启动SpringIOC容器对Bean定义的解析过程,registerBeanDefinitions()方法的源码如下:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 创建BeanDefinitionDocumentReader解析doc
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
// 具体的解析实现过程 位置1
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
Bean配置资源的载入解析分为以下两个过程:
-
首先通过调用xml解析器将Bean配置信息转换得到Document对象,但是这些Document对象并没有按照Spring的Bean规则进行解析。
-
其次,在完成XML解析之后,按照SpringBean的定义规则对Document对象进行解析,其解析过程是在接口 BeanDefinitionDocumentReader 的实现类 DefaultBeanDefinitionDocumentReader 中实现(位置1)。
9,解析Document对象
BeanDefinitionDocumentReader 接口通过 registerBeanDefinitions() 方法调用其实现类 DefaultBeanDefinitionDocumentReader 对 Document 对象进行解析,代码如下:
// 解析Document对象
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}
protected void doRegisterBeanDefinitions(Element root) {
//具体的解析过程由 BeanDefinitionParserDelegate 实现,
//BeanDefinitionParserDelegate 中定义了 Spring Bean 定义 XML 文件的各种元素
BeanDefinitionParserDelegate parent = this.delegate;
//创建 BeanDefinitionParserDelegate,用于完成真正的解析过程
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
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()) { ... }
return;
}
}
}
// 在解析Bean定义之前,进行自定义的解析,增强解析过程的可扩展性
preProcessXml(root);
// 从Document的根元素开始解析Bean定义的Document对象 位置1
parseBeanDefinitions(root, this.delegate);
// 在解析Bean定义之后,进行自定义的解析,增加解析过程的可扩展性
postProcessXml(root);
this.delegate = parent;
}
虽然解析的过程是由DefaultBeanDefinitionDocumentReader 调用,但是实际的解析过程则是由 BeanDefinitionParserDelegate 来完成(位置1)。
10,开始解析
接上步骤中的位置1:由 BeanDefinitionParserDelegate 来完成Document的解析过程,代码如下:
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
// 获取Bean定义的Document对象根元素的所有字节点
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
// //使用 Spring 的 Bean 规则解析元素节点 位置1
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
从位置1进去,代码如下:
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
// 如果元素时<Import> 导入元素,进行导入解析
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
// 如果元素时<Alias> 导入元素,进行别名解析
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//元素节点既不是导入元素,也不是别名元素,即普通的<Bean>元素,
//按照 Spring 的 Bean 规则解析元素
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);// 位置1
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
doRegisterBeanDefinitions(ele);
}
}
可以看到,代码中根据标签类型,对标签内的元素进行解析。下面将以标签为例,一起来看一下是如何载入的。
11,加载Bean
从上步骤的位置1点进来,记住这个位置,稍后我们还会回来:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 第一行 位置1
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
...
}
我们这里只看该方法的第一行:
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
再进来就是解析bean标签的核心方法了,这个方法主要处理bean元素的id,nam和别名属性:
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// 获取bean元素中的id属性
String id = ele.getAttribute(ID_ATTRIBUTE);
// 读取bean元素中的属性值
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
// 新建集合存储别名,如果配置了别名就尝试获取
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
// 如果 bean元素中没有设置id属性时,将别名中第一个值赋值给beanName
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
if (logger.isTraceEnabled()) {...}
}
if (containingBean == null) {
// 检查bean元素所配置id、name或别名是否重复
checkNameUniqueness(beanName, aliases, ele);
}
// 详细对bean元素中配置的bean定义进行解析的地方 位置1
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
//如果<Bean>元素中没有配置 id、别名或者 name,且没有包含子元素
//<Bean>元素,为解析的 Bean 生成一个唯一 beanName 并注册
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
//如果<Bean>元素中没有配置 id、别名或者 name,且包含了子元素
//<Bean>元素,为解析的 Bean 使用别名向 IOC 容器注册
beanName = this.readerContext.generateBeanName(beanDefinition);
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (logger.isTraceEnabled()) {
...
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
截止到这里,Spring已经完成了对id,name属性的解析,bean中详细内容的解析就交由 parseBeanDefinitionElement()这个方法来完成(位置1),接下来我们一起看一下这个方法的源码:
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
// 记录要解析的bean
this.parseState.push(new BeanEntry(beanName));
// 记录 class 属性的内容 如:com.xx.pojo.Person
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
// 如果bean标签中配置了parent属性,那么就记录
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
/* 根据bean元素配置的class名称和parent属性值创建BeanDefinition
为载入Bean定义信息做准备*/
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 对当前的bean元祖中配置的一些属性进行解析和设置
// 如配置的单例属性(Singleton)等
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
// 为bean元素解析的bean设置 description 标签信息
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
// 对bean元素的元信息属性解析
parseMetaElements(ele, bd);
// 解析lookup-method属性和replaced-method
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// 解析构造方法
parseConstructorArgElements(ele, bd);
// 解析property标签
parsePropertyElements(ele, bd);// 位置1
// 解析bean元素的 qualifier 标签
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
catch (ClassNotFoundException ex) {
...
}
finally {
this.parseState.pop();
}
return null;
}
只要对配置文件比较熟悉,通过以上代码的分析,就会明白我们再Spring配置文件中bean元素中配置的属性就是通过该方法解析和设置到Bean中去的。
注意:在解析bean元素过程中并没有创建和实例化Bean对象,只是创建了Bean对象的定义类BeanDefiniton,将bean元素中的配置信息设置到BeanDefinition中作为记录,当依赖注入时才使用这些记录信息创建和实例化具体的Bean对象。
我们再使用bean标签进行配置时,使用最多的就是property,接下来我们一起看一下property标签是如何配置的(代码位置1)。
12,载入property标签元素
BeanDefinitionParserDelegate 在解析bean时调用parsePropertyElements()方法来完成对property标签的解析(上步骤代码位置1)。源码如下:
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
// 获取bean元素中所有子元素
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 如果子元素是property元素,则调用解析方法解析
if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
parsePropertyElement((Element) node, bd);// 位置1
}
}
}
这里可以看到,加载流程为:首先获取该bean标签中所有的元素,如果元素是property,则调用解析方法解析该标签:
// 解析 property
public void parsePropertyElement(Element ele, BeanDefinition bd) {
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
if (!StringUtils.hasLength(propertyName)) {
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
this.parseState.push(new PropertyEntry(propertyName));
try {
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
// 解析获取property的值
Object val = parsePropertyValue(ele, bd, propertyName);
// 根据property的名字和值创建property实例
PropertyValue pv = new PropertyValue(propertyName, val);
// 解析property标签内的属性
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
bd.getPropertyValues().addPropertyValue(pv);
}
finally {
this.parseState.pop();
}
}
这里就不详细往里看了,时间有限…
4-12步总结:
经过4-12步的介绍,我们有了一个清晰的脉络:
- 首先bean.xml文件会首先被我们所配置的资源加载器加载【ClassPathXmlApplicationContext】
- 接着由我们配置的资源读取器【XmlBeanDefinitionReader】,加载我们所配置的配置文件
- 由【DefaultBeanDefinitionDocumentReader 】将我们配置的资源解析为Document对象
- 由【BeanDefinitionParserDelegate 】来完成Document的解析过程
- 最终我们将xml中的配置信息转换成了Spring IOC所识别的数据结构——BeanDefinition
通过这几步对配置资源的解析后,IOC容器大致完成了管理bean对象的准备工作,即初始化过程,但是最重要的依赖注入等操作还没有发生,现在在IOC容器中BeanDefinition存储的只是一些静态信息,接下来需要向容器中注册Bean的定义信息才能全部完成IOC容器的初始化过程。
13,让我们回到11步
从上面的流程中我们知道,Spring在11-12步中,完成了对配置文件的加载工作,并将Bean配置信息成功转换为了BeanDefinition,接下来 BeanDefinitionParserDelegate 将分封装了 BeanDefinition 的BeanDefinitionHold对象返回。
// DefaultBeanDefinitionDocumentReader中的processBeanDefinition方法
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 11步中的位置1
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 向容器中注册解析的 Bean 位置1
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
...
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
来看代码的位置1,由 BeanDefinitionReaderUtils 的 registerBeanDefinition() 方法向IOC容器中注册解析的 Bean。
14,注册Bean
从上步骤中的位置1点进去,
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// 获取解析的BeanDefinition的名称
String beanName = definitionHolder.getBeanName();
// 向容器中注册 位置1
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 如果解析的BeanDefinition有别名,则注册别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
当 BeanDefinitionReaderUtils 调用 registerBeanDefinition() 方法时,真正完成注册操作的是 DefaultListableBeanFactory 接下来我们一起看进去(位置1):
// DefaultListableBeanFactory 解析注册Bean
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(...);
}
}
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
if (logger.isInfoEnabled()) {
...
}
}
else if (!beanDefinition.equals(existingDefinition)) {
if (logger.isDebugEnabled()) {
...
}
}
else {
if (logger.isTraceEnabled()) {
...
}
}
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
if (hasBeanCreationStarted()) {
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
removeManualSingletonName(beanName);
}
}
else {
// 位置1
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
else if (isConfigurationFrozen()) {
clearByTypeCache();
}
}
这个方法很长,简而言之就是做了一件事: 使用一个ConcurrentHashMap(this.beanDefinitionMap)的集合对象存放IOC容器解析的BeanDefinition(位置1)。
小总结:
至此,Bean配置信息中配置的Bean就被解析完成,且注册到IOC容器中,被容器管理起来,真正完成了IOC容器初始化所做的全部工作。现在IOC容器中已经建立了整个Bean的配置信息,这些BeanDefinition信息已经可以使用,并可以被检索,IOC容器的作用就是对这些注册的Bean定义信息进行维护和处理。这些注册的Bean定义信息是IOC容器控制反转的基础,正式有了这些注册的数据,容器才可以进行依赖注入。
15,Bean初始化之前的准备工作
描述请看代码
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// 准备工作
prepareRefresh();
// 将xml解析为 BeanDefinition 获取BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
/*
准备工作:设置BeanFactory的类加载器、支持表达式解析器、
添加部分BeanFactoryProcessor等
*/
prepareBeanFactory(beanFactory);
try {
// 模板方法:准备工作完成后的后置处理工作
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// 调用Bean工厂后置处理器
invokeBeanFactoryPostProcessors(beanFactory);
// 注册Bean的后置处理器
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// 初始化MessageSource组件(做国际化功能)
initMessageSource();
// 初始化事件派发器
initApplicationEventMulticaster();
onRefresh();
// 注册监听器
registerListeners();
// 初始化单例Bean 位置1
finishBeanFactoryInitialization(beanFactory);
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
...
}
destroyBeans();
cancelRefresh(ex);
throw ex;
}
finally {
resetCommonCaches();
contextRefresh.end();
}
}
}
16,初始化单实例Bean
我们来一起看一下上步骤中的位置1,在finishBeanFactoryInitialization(beanFactory)方法中,会完成初始化单例Bean的工作,我们来一起看一下源码:
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
...
// 实例化所有剩余的单例Bean 位置1
beanFactory.preInstantiateSingletons();
}
这里我们只看最后一行代码:beanFactory.preInstantiateSingletons();
该方法使用DefaultListableBeanFactory的preInstantiateSingletons()方法进行Bean的初始化工作:
@Override
public void preInstantiateSingletons() throws BeansException {
if (logger.isTraceEnabled()) {
logger.trace("Pre-instantiating singletons in " + this);
}
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(
(PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
}
else {
getBean(beanName);//位置1
}
}
...
}
可以看到在该方法中获取了Bean的定义信息并加载bean:
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
// getBean调用doGetBean
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
String beanName = transformedBeanName(name);
Object beanInstance;
// 1,先获取缓存中保存的单实例Bean,如果能获取到
// 说明这个Bean之前被创建过
Object sharedInstance = getSingleton(beanName);
// 2,缓存中没有拿到
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("...");
}
else {
logger.trace("...");
}
}
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if (args != null) {
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else if (requiredType != null) {
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
else {
return (T) parentBeanFactory.getBean(nameToLookup);
}
}
// 3,标记当前Bean已经被创建
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
.tag("beanName", name);
try {
if (requiredType != null) {
beanCreation.tag("beanType", requiredType::toString);
}
// 4,获取Bean的定义信息
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
//,5,获取当前Bean依赖的其他Bean
// 如果有依赖其他bean,则通过getBean获取
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new ...
}
registerDependentBean(dep, beanName);
try {
// 如果有则通过getBean获取
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
...
}
}
}
// 6, 启动创建Bean的流程
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
String scopeName = mbd.getScope();
if (!StringUtils.hasLength(scopeName)) {
throw new ...
}
Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new ...
}
try {
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new ScopeNotActiveException(beanName, scopeName, ex);
}
}
}
catch (BeansException ex) {
...
}
finally {
beanCreation.end();
}
}
return adaptBeanInstance(name, beanInstance, requiredType);
}
通过上面这个方法我们可以得到的信息是:在进行Bean的初始化过程中,首先会使用 getSingleton(beanName);方法尝试从缓存中获取Bean,代码如下:
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
// 一级缓存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 二级缓存
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
// 三级缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
如果没有获取到则会将当前Bean已创建状态,然后执行创建Bean的操作。
17,创建Bean
从createBean(beanName, mbd, args)方法看进来:
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
...
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
...
}
我们来只看这一行的doCreateBean()方法:
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
...
// 位置1(初始化的第一步)
instanceWrapper = createBeanInstance(beanName, mbd, args);
...
// 位置2
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
...
// 位置3
populateBean(beanName, mbd, instanceWrapper);
// 位置4
exposedObject = initializeBean(beanName, exposedObject, mbd);
...
...
return exposedObject;
}
这里只留下几个关键代码进行介绍:
- 位置1:这是初始化的第一步,就是将Bean创建出来,需要注意的是,此处只是将Bean创建了出来
- **位置2(重点!!!)**这个地方我们在下个步骤解释
- 位置3:populateBean(beanName, mbd, instanceWrapper) 完成Bean属性赋值,主要是配置前、后置处理器,并为属性利用setter等进行赋值
- 位置4:initializeBean(beanName, exposedObject, mbd) 执行bean的初始化
- 执行Bean实现的Aware接口的方法
- 执行后置处理器初始化之前的方法
- 执行初始化方法
- 执行后置处理器初始化之后的方法
- 注册bean的销毁方法
18,循环依赖
上步骤中的位置2是解决循环依赖的关键,我们来一起看一下这个方法的内容:
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
// 加到三级缓存中
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
这段代码发生在createBeanInstance之后,也就是说单例对象此时已经被创建出来(调用了构造器)。这个对象已经被生产出来了,虽然还不完美(还没有进行初始化的第二步和第三步),但是已经能被人认出来了(根据对象引用能定位到堆中的对象),所以Spring此时将这个对象提前曝光出来让大家认识,让大家使用。
这样做有什么好处呢?让我们来分析一下“A的某个field或者setter依赖了B的实例对象,同时B的某个field或者setter依赖了A的实例对象”这种循环依赖的情况。A首先完成了初始化的第一步,并且将自己提前曝光到singletonFactories中,此时进行初始化的第二步,发现自己依赖对象B,此时就尝试去get(B),发现B还没有被create,所以走create流程,B在初始化第一步的时候发现自己依赖了对象A,于是尝试get(A),尝试一级缓存singletonObjects(肯定没有,因为A还没初始化完全),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓存singletonFactories,由于A通过ObjectFactory将自己提前曝光了,所以B能够通过ObjectFactory.getObject拿到A对象(虽然A还没有初始化完全,但是总比没有好呀),B拿到A对象后顺利完成了初始化阶段1、2、3,完全初始化之后将自己放入到一级缓存singletonObjects中。此时返回A中,A此时能拿到B的对象顺利完成自己的初始化阶段2、3,最终A也完成了初始化,进去了一级缓存singletonObjects中,而且更加幸运的是,由于B拿到了A的对象引用,所以B现在hold住的A对象完成了初始化。
注:该步骤参考博客:https://blog.csdn.net/u010853261/article/details/77940767
19,创建完成
创建完Bean之后将会调用getSingleton(String, ObjectFactory<?>)方法获取单实例Bean,并在该方法中最终调用addSingleton方法将该Bean添加到缓存(singletonObjects)中:
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
// 添加到一级缓存中
this.singletonObjects.put(beanName, singletonObject);
// 从二级缓存中删除
this.singletonFactories.remove(beanName);
// 从三级缓存中删除
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
的实例对象”这种循环依赖的情况。A首先完成了初始化的第一步,并且将自己提前曝光到singletonFactories中,此时进行初始化的第二步,发现自己依赖对象B,此时就尝试去get(B),发现B还没有被create,所以走create流程,B在初始化第一步的时候发现自己依赖了对象A,于是尝试get(A),尝试一级缓存singletonObjects(肯定没有,因为A还没初始化完全),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓存singletonFactories,由于A通过ObjectFactory将自己提前曝光了,所以B能够通过ObjectFactory.getObject拿到A对象(虽然A还没有初始化完全,但是总比没有好呀),B拿到A对象后顺利完成了初始化阶段1、2、3,完全初始化之后将自己放入到一级缓存singletonObjects中。此时返回A中,A此时能拿到B的对象顺利完成自己的初始化阶段2、3,最终A也完成了初始化,进去了一级缓存singletonObjects中,而且更加幸运的是,由于B拿到了A的对象引用,所以B现在hold住的A对象完成了初始化**。
注:该步骤参考博客:https://blog.csdn.net/u010853261/article/details/77940767
19,创建完成
创建完Bean之后将会调用getSingleton(String, ObjectFactory<?>)方法获取单实例Bean,并在该方法中最终调用addSingleton方法将该Bean添加到缓存(singletonObjects)中:
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
// 添加到一级缓存中
this.singletonObjects.put(beanName, singletonObject);
// 从二级缓存中删除
this.singletonFactories.remove(beanName);
// 从三级缓存中删除
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}