07. Spring IoC源码解析

Posted IT BOY

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了07. Spring IoC源码解析相关的知识,希望对你有一定的参考价值。

07 Spring IoC源码解析

目录

Pt1 IoC 和 DI

Pt2 IoC容器核心类

Pt2.1 ApplicationContext

Pt2.2 BeanFactory

Pt2.3 Environment

(1) Profile

(2) Properties

Pt2.4 ResourceLoader

Pt2.5 BeanDefinition

Pt2.6 BeanDefinitionReader

Pt2.7 BeanDefinitionHolder

Pt2.8 Resource

Pt2.9 BeanDefinitionParserDelegate

Pt3 IoC容器初始化

Pt3.1 寻找入口

(1) DispatcherServlet#init()

(2) ApplicationContext入口

Pt3.2 获得配置路径

Pt3.3 开始启动容器

Pt3.4 创建容器

Pt3.5 载入配置路径

Pt3.6 分配路径处理策略

Pt3.7 解析配置文件路径

Pt3.8 开始读取配置资源

Pt3.9 准备文档对象

Pt3.10 分配解析策略

Pt3.11 将配置载入内存

Pt 3.12 解析元素

Pt3.13 解析元素

Pt3.14 分配注册策略

Pt3.15 向容器注册

Pt3.16 整体流程图

参考资料


Pt1 IoC 和 DI

IoC(Inversion of Control,控制反转)就是把原来代码里需要实现的对象创建、依赖,反转给容器来帮助管理。我们需要创建一个容器(FactoryBean),同时需要一种描述来让容器知道要创建的对象与对象的关系,这个描述最具体的表现就是Spring配置文件。

DI(Dependency Injection,依赖注入)就是指对象被动接受依赖类而不需要自己主动去找。换句话说,就是指对象不是从容器中查找他依赖的类,而是在容器实例化对象时主动将它依赖的类注入给他。

 


Pt2 IoC容器核心类

org.springframework.context.ApplicationContext接口代表Spring IoC容器,主要负责bean的实例化、配置、装配,简而言之,Spring IoC容器是管理这些bean的。容器如何知道哪些对象要进行实例化、配置和装配的呢?是通过读取配置文件元数据来达到这个效果的,配置文件元数据是用xml配置、Java注解和Java代码配置来表示的。程序只需要向Spring容器提供配置元数据,Spring容器就能在我们的应用中实例化、配置和装配这些对象。org.springframework.beans和org.springframework.context包是Spring IoC容器的基础。Spring提供了很多Application接口的实现。

 

Pt2.1 ApplicationContext

ApplicationContext是Spring的IoC容器,他定义了容器的基本行为(BeanFactory能力),执行Bean定义的解析和加载。除此之外,他还提供了以下附加功能:

  1. 支持信息源,可以实现国际化(实现MessageSource接口);

  2. 访问资源(实现ResourcePatternResolver接口);

  3. 支持应用事件(实现ApplicationEventPublisher接口);

 

ApplicationContext的类图如下:

从上图就能很清楚的看出ApplicationContext继承的接口分为五类:

  • BeanFactory:提供了能够管理任何对象的高级配置机制,约定了IoC容器的行为,这个接口是Spring框架中比较重要的一个接口。

    • ListableBeanFactory:从该接口的名字就能知道,该接口除了拥有BeanFactory的功能外,该接口还有能列出factory中所有bean的实例的能力。

    • HierarchicalBeanFactory:该接口除了拥有BeanFactory的功能外,还提供了BeanFactory分层的机制,查找bean的时候,除了在自身BeanFactory查找外,如果没有查找到,还会在父级BeanFactory进行查找。

  • MessageSource:消息资源的处理,用于国际化。

  • ApplicationEventPublisher:用于处理事件发布机制。

  • EnvironmentCapable:提供了Environment的访问能力。

  • ResourceLoader:用于加载资源的策略接口(例如类路径下的资源、系统文件下的资源等等)。

    • ResourcePatternResolver:用于将位置模式(例如Ant风格的路径模式)解析成资源对象的策略接口。classpath*:前缀能匹配所以类路径下的资源。

 

ApplicationContext源码解析:

 public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
         MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
 ​
     // 获取ApplicationContext的唯一ID
     @Nullable
     String getId();
 ​
     // 该上下文所属的已经部署了的应用名称,默认为""
     String getApplicationName();
 ​
     // 展示该上下文相对友好的名称
     String getDisplayName();
 ​
     // 该上下文第一次加载时间
     long getStartupDate();
 ​
     // 获得父级ApplicationContext
     @Nullable
     ApplicationContext getParent();
     
     // 获取BeanFactory的能力
     AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
 }

 


Pt2.2 BeanFactory

BeanFactory(Bean工厂)是SpringIoC容器的基础,是典型的工厂模式。他为开发者管理对象之间的依赖关系提供了很多便利和基础功能,IoC则将处理事情的责任从应用程序代码转移到框架中完成。

 

Spring中BeanFactory类图如下所示。

BeanFactory作为顶层接口类,定义了IoC容器的基本规范,他有3个重要子类:ListableBeanFactory、HierarchicalBeanFactory和AutowireCapableBeanFactory。但是从上面的类图上可以看到,最下层的实现类只有DefaultListableBeanFactory,它实现了所有接口的能力。

既然只有一个实现类,那为什么在顶层接口和底层实现之间又衍生出多个接口类呢?从下面代码分析中我们可以看出,每个接口都定义了特定的使用场景,主要是为了区分在Spring内部操作过程中对象的传递和转化,对对象的数据访问所做的限制。他们共同定义了Bean的集合、Bean之间的关系和Bean的行为。

  • ListableBeanFactory表示Bean支持列表化;

  • HierarchicalBeanFactory表示Bean有继承关系;

  • AutowireCapableBeanFactory定义Bean的自动装配规则。

 

BeanFactory源码定义如下:

 public interface BeanFactory {
 ​
     /**
      * 对FactoryBean的转义定义。
      * 因为如果使用Bean的名字检索FactoryBean得到的对象是工厂生成的对象,如果需要得到工厂本身,需要转义。
      */
     String FACTORY_BEAN_PREFIX = "&";
 ​
     /**
      * 根据Bean的名字,获取在IoC容器中得到的Bean的实例。
      */
     Object getBean(String name) throws BeansException;
 ​
     /**
      * 根据Bean的名字和Class类型,获取在IoC容器中得到的Bean的实例,增加了类型安全验证机制。
      */
     <T> T getBean(String name, Class<T> requiredType) throws BeansException;
 ​
     /**
      * 根据Bean的名字和参数,获取在IoC容器中得到的Bean的实例。
      */
     Object getBean(String name, Object... args) throws BeansException;
 ​
     /**
      * 根据Class类型获取IoC容器中的实例。
      */
     <T> T getBean(Class<T> requiredType) throws BeansException;
 ​
     /**
      * 根据Class类型和参数,获取在IoC容器中得到的Bean的实例。
      */
     <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
 ​
     /**
      * 获取Bean的提供者。
      */
     <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
     <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
 ​
     /**
      * 提供对Bean的检索,看看在IoC容器中是否包含这个名称的Bean。
      */
     boolean containsBean(String name);
 ​
     /**
      * 根据Bean的名称得到对应的实例,同时判断Bean是不是Singleton。
      */
     boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
 ​
     /**
      * 根据Bean的名称得到对应的实例,同时判断Bean是不是Prototype。
      */
     boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
 ​
     /**
      * 判断对应名称的Bean是否匹配给定的类型。
      */
     boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
 ​
     /**
      * 判断对应名称的Bean是否匹配给定的类型。
      */
     boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
 ​
     /**
      * 得到Bean实例的Class类型。
      */
     @Nullable
     Class<?> getType(String name) throws NoSuchBeanDefinitionException;
 ​
     /**
      * 得到Bean实例的Class类型。
      */
     @Nullable
     Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;
 ​
     /**
      * 得到Bean的别名,如果根据别名进行搜索,那么其原名也会被检索出来。
      */
     String[] getAliases(String name);
 }

BeanFactory对IoC容器的基本行为做了定义,比如从容器中获取、判断Bean是否存在等,BeanFactory不关心Bean是如何定义和加载的,他只是直接把容器中已经加载好的Bean拿来使用。

 


Pt2.3 Environment

Environment用来表示整个应用运行时的环境,为了更形象地理解Environment,你可以把Spring应用的运行时简单地想象成两个部分:一个是Spring应用本身,一个是Spring应用所处的环境,而Environment这个接口,就是对这个所处的环境的概念性建模。

Environment在容器中是一个抽象的集合,是指应用环境的2个方面:profiles和properties。

 

(1) Profile

profile配置是一个被命名的、bean定义的逻辑组,这些bean只有在给定的profile配置激活时才会注册到容器。profile的使用是为了能以最简单的方式切换配置文件,通常是简化多个环境不同配置的复杂。

比如项目中经常遇到的开发、测试、生产等环境的切换。

 <beans profile="test">
     <context:property-placeholder location="/WEB-INF/test.properties" />
 </beans>
 <beans profile="dev">
     <context:property-placeholder location="/WEB-INF/dev.properties" />
 </beans>

启动是指定运行环境:

 JAVA_OPTS="-Dspring.profiles.active=test"

 

Environment环境对象的作用,对于profiles配置来说,它能决定当前激活的是哪个profile配置,和哪个profile是默认。

一个profile就是一组Bean定义的逻辑分组。

  • 这个分组,也就这个profile,被赋予一个命名,就是这个profile名字。

  • 只有当一个profile处于active状态时,它对应的逻辑上组织在一起的这些Bean定义才会被注册到容器中。

  • Bean添加到profile可以通过XML定义方式或才annotation注解方式。

  • Environment对于profile所扮演的角色是用来指定哪些profile是当前活跃的缺省。

 

(2) Properties

properties属性可能来源于properties文件、JVM properties、system环境变量、JNDI、servlet context parameters上下文参数、专门的properties对象,Maps等等。Environment对象的作用,对于properties来说,是提供给用户方便的服务接口、方便撰写配置、方便解析配置。

  • 配置属性源。

  • 从属性源中获取属性。

 

容器(ApplicationContext)所管理的bean如果想直接使用Environment对象访问profile状态或者获取属性,可以有两种方式

(1)实现EnvironmentAware接口。

(2)@Inject或者@Autowired一个Environment对象。

绝大数情况下,bean都不需要直接访问Environment对象,而是通过类似@Value注解的方式把属性值注入进来。

 

EnvironmentCapable源码如下:

 // 应用运行时环境变量
 public interface Environment extends PropertyResolver {
 ​
     // 返回当前环境中激活状态的profiles配置的属性集合
     String[] getActiveProfiles();
 ​
     // 返回当前环境中默认的profiles配置的属性集合,如果当前环境没有显示的指明要激活的Profiles环境,
     // 通过getDefaultProfiles()可以获取默认的Profiles属性定义。
     String[] getDefaultProfiles();
 ​
     // 当前环境激活的Profiles或者默认的Profiles是否包含指定的Profiles。
     @Deprecated
     boolean acceptsProfiles(String... profiles);
 ​
     // 当前环境激活的Profiles和指定Profiles是否匹配
     boolean acceptsProfiles(Profiles profiles);
 }

 

除此之外和Environment相关的有两个接口:EnvironmentAware和EnvironmentCapable。

EnvironmentAware接口提供了访问配置信息的能力,源码如下:

 /**
  * 凡注册到Spring容器内的bean,实现了EnvironmentAware接口重写setEnvironment方法后,在工程启动时可以获得application.properties的配置文件配置的属性值。
  */
 public interface EnvironmentAware extends Aware {
     
     // 获取和设置配置属性
     void setEnvironment(Environment environment);
 }
Eg.
 @Configuration
 public class MyProjectc implements EnvironmentAware {
     @Override
     public void setEnvironment(Environment environment) {
         String projectName = environment.getProperty("project.name");
     }
 } 

 

EnvironmentCapable提供了访问Environment的能力,源码如下:

 /**
  * 实现了此接口的类有应该有一个Environment类型的域,并且可以通过getEnvironment方法取得。
  */
 public interface EnvironmentCapable {
 ​
     // 获取Environment对象
     Environment getEnvironment();
 }

 


Pt2.4 ResourceLoader

ResourceLoader该接口是用来加载资源(例如类路径或者文件系统中的资源)的策略接口。

/**
 * 接口是用来加载资源(例如类路径或者文件系统中的资源)的策略接口。
 * 该接口只有简单明了的两个方法,一个是用来获取指定位置的资源,一个用于获取资源加载器所使用的类加载器。
 * Resource是从实际类型的底层资源(例如文件、类路径资源)进行抽象的资源描述符。
 */
public interface ResourceLoader {

	/** Pseudo URL prefix for loading from the class path: "classpath:". */
	String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;

	// 根据指定的位置获取资源
	Resource getResource(String location);

	// 获取该资源加载器所使用的类加载器
	@Nullable
	ClassLoader getClassLoader();
}

 

其中,子接口ResourcePatternResolver用于将一个位置模式的字符串解析为资源。

/**
 * 接口用于解析一个位置模式(例如Ant风格的路径模式)。
 */
public interface ResourcePatternResolver extends ResourceLoader {

	String CLASSPATH_ALL_URL_PREFIX = "classpath*:";

	// 将给定的位置模式解析成资源对象
	Resource[] getResources(String locationPattern) throws IOException;
}

 

我们再看看Resource源码的定义:

/**
 * Resource接口是对资源描述的定义
 */
public interface Resource extends InputStreamSource {

	// 资源实际上是否存在
	boolean exists();
	// 资源是否可读
	default boolean isReadable() {
		return exists();
	}
	// 检查资源是否为打开的流
	default boolean isOpen() {
		return false;
	}
	// 资源是否为文件系统上的一个文件
	default boolean isFile() {
		return false;
	}

	// 获取url
	URL getURL() throws IOException;
	// 获取URI
	URI getURI() throws IOException;
	// 获取文件
	File getFile() throws IOException;

	// 获取ReadableByteChannel用于读取资源
	default ReadableByteChannel readableChannel() throws IOException {
		return Channels.newChannel(getInputStream());
	}

	// 资源的内容的长度
	long contentLength() throws IOException;
	// 资源的最后修改时间
	long lastModified() throws IOException;
	// 相对于使用当前的资源创建一个新的资源
	Resource createRelative(String relativePath) throws IOException;
	// 获取资源的文件名
	@Nullable
	String getFilename();
	// 获取资源的描述信息
	String getDescription();
}

 

PathMatchingResourcePatternResolver

我们在介绍ResourceLoader的一个实现类——PathMatchingResourcePatternResolver,在ApplicationContext的初始化过程中会使用他来完成资源文件的解析。

/**
 * 一个{@link ResourcePatternResolver}实现,它能够将指定的资源位置路径解析为一个或多个匹配的资源。
 */
public class PathMatchingResourcePatternResolver implements ResourcePatternResolver {
	public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
		Assert.notNull(resourceLoader, "ResourceLoader must not be null");
		this.resourceLoader = resourceLoader;
	}
    
    // 将传入的资源文件路径解析为资源。
	// 传入locationPattern,是资源文件路径,比如{classpath*:applicationContext.xml}
	@Override
	public Resource[] getResources(String locationPattern) throws IOException {
		Assert.notNull(locationPattern, "Location pattern must not be null");

		// 处理[classpath*:]开头的文件
		if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
			// 资源路径中存在* { }这3中模糊匹配的情况下
			if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
				// 通过模式匹配获取资源路径的所有资源
				return findPathMatchingResources(locationPattern);
			}
			else {
				// 根据全路径匹配获取资源
				return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
			}
		}
		else {
			// Generally only look for a pattern after a prefix here,
			// and on Tomcat only after the "*/" separator for its "war:" protocol.
			int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 :
					locationPattern.indexOf(':') + 1);
			if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
				// a file pattern
				return findPathMatchingResources(locationPattern);
			}
			else {
				// a single resource with the given name
				return new Resource[] {getResourceLoader().getResource(locationPattern)};
			}
		}
	}
    
    .....
}

其核心就是getResources(),包含了对各种资源文件路径方式的解析逻辑,里面具体的逻辑就不研究了。

 


Pt2.5 BeanDefinition

Spring IoC容器用来管理各种Bean对象及其互相关系,BeanDefinition就是用来定义Bean对象及其关系的。

BeanDefinition主要定义了以下信息:

  1. BeanDefinition 包含了我们对 bean 做的配置,比如 XML<bean/>标签的形式进行的配置;

  2. Spring 将我们对 bean 的定义信息进行了抽象,抽象后的实体就是 BeanDefinition,并且 Spring 会以此作为标准对 bean 进行创建。

  3. BeanDefinition 包含以下元数据:

    1. 一个全限定类名,通常来说,就是对应 bean 的全限定类名。

    2. bean 的行为配置元素,这些元素展示了这个 bean 在容器中是如何工作的,包括 scope,lifecycle callbacks(生命周期回调)等等。

    3. 这个 bean 的依赖信息。

    4. 一些其他配置信息,比如我们配置了一个连接池的对象,那么我们还会配置它池子的大小,最大连接数等等。

 

我们看源码中BeanDefinition是如何定义Bean对象的。

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

	// 单例、原型标识符
	String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
	String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;

	// 标识 Bean 的类别,分别对应 用户定义的 Bean、来源于配置文件的 Bean、Spring 内部的 Bean
	int ROLE_APPLICATION = 0;
	int ROLE_SUPPORT = 1;
	int ROLE_INFRASTRUCTURE = 2;

	// 设置、返回 Bean 的父类名称
	void setParentName(@Nullable String parentName);
	@Nullable
	String getParentName();

	// 设置、返回 Bean 的 className
	void setBeanClassName(@Nullable String beanClassName);
	@Nullable
	String getBeanClassName();

	// 设置、返回 Bean 的作用域
	void setScope(@Nullable String scope);
	@Nullable
	String getScope();

	// 设置、返回 Bean 是否懒加载
	void setLazyInit(boolean lazyInit);
	boolean isLazyInit();

	// 设置、返回当前 Bean 所依赖的其它 Bean 名称。
	void setDependsOn(@Nullable String... dependsOn);
	@Nullable
	String[] getDependsOn();

	// 设置、返回 Bean 是否可以自动注入。只对 @Autowired 注解有效
	void setAutowireCandidate(boolean autowireCandidate);
	boolean isAutowireCandidate();

	// 设置、返回当前 Bean 是否为主要候选 Bean 。
	// 当同一个接口有多个实现类时,通过该属性来配置某个 Bean 为主候选 Bean。
	void setPrimary(boolean primary);
	boolean isPrimary();

	// 定义创建该Bean对象的工厂类
	void setFactoryBeanName(@Nullable String factoryBeanName);
	@Nullable
	String getFactoryBeanName();

	// 创建该Bean对象的工厂方法
	void setFactoryMethodName(@Nullable String factoryMethodName);
	@Nullable
	String getFactoryMethodName();

	// 返回此bean的构造函数参数值。
	ConstructorArgumentValues getConstructorArgumentValues();
	default boolean hasConstructorArgumentValues() {
		return !getConstructorArgumentValues().isEmpty();
	}

	// 获取普通属性集合
	MutablePropertyValues getPropertyValues();
	default boolean hasPropertyValues() {
		return !getPropertyValues().isEmpty();
	}

	// 定义初始化Bean的方法名称
	void setInitMethodName(@Nullable String initMethodName);
	@Nullable
	String getInitMethodName();

	// 定义销毁Bean的方法名称
	void setDestroyMethodName(@Nullable String destroyMethodName);
	@Nullable
	String getDestroyMethodName();

	// 设置和获取这个bean的应用
	void setRole(int role);
	int getRole();

	// 设置和获取对bean定义的可读描述。
	void setDescription(@Nullable String description);
	@Nullable
	String getDescription();


	// Read-only attributes

	// Bean类型
	ResolvableType getResolvableType();

	// 是否为单例、原型和抽象类
	boolean isSingleton();
	boolean isPrototype();
	boolean isAbstract();

	// 返回该bean定义来自的资源的描述(用于在出现错误时显示上下文)
	@Nullable
	String getResourceDescription();
	@Nullable
	BeanDefinition getOriginatingBeanDefinition();
}

 


Pt2.6 BeanDefinitionReader

BeanDefinitionReader 的作用是读取 Spring 配置文件中的内容,将其转换为 IoC 容器内部的数据结构:BeanDefinition。

为了保证足够的扩展性和灵活性,Spring对Bean的解析过程设计的非常复杂,我们看看BeanDefinitionReader接口的定义,具体实现类我们在ApplicationContext实例化过程中在介绍。

public interface BeanDefinitionReader {

	// 返回Bean工厂以向其注册Bean定义。
	BeanDefinitionRegistry getRegistry();

	// 返回资源加载器以用于资源位置。
	//  可以检查ResourcePatternResolver接口并进行相应的转换,以针对给定的资源模式加载多个资源。
	//  一个null返回值表明,绝对资源加载不适用于这个bean定义阅读器。这主要用于从bean定义资源中导入其他资源,
	//  例如,通过XML bean定义中的“ import”标记。但是,建议相对于定义资源应用此类导入;
	//  只有明确的完整资源位置才会触发绝对资源加载。
	@Nullable
	ResourceLoader getResourceLoader();

	// 返回用于Bean类的类加载器。
	@Nullable
	ClassLoader getBeanClassLoader();

	// 返回BeanNameGenerator用于匿名Bean(未指定显式Bean名称)。
	BeanNameGenerator getBeanNameGenerator();
	
	//从指定的资源加载bean定义。
	int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;
	int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException;

	//从指定的资源位置加载bean定义。
	//该位置也可以是位置模式,前提是此bean定义读取器的ResourceLoader是ResourcePatternResolver。
	int loadBeanDefinitions(String location) throws BeanDefinitionStoreException;
	int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException;
}

 


Pt2.7 BeanDefinitionHolder

简单的说,BeanDefinitionHolder就是对一个BeanDefinition的持有,只是除了BeanDefinition,他包装了更多的元素。将BeanDefinition封装起来,使用起来更加方便的同时,也支持Spring应对更多的变化。

public class BeanDefinitionHolder implements BeanMetadataElement {

	//持有BeanDefinition
	private final BeanDefinition beanDefinition;
	//BeanDefinition的名称
	private final String beanName;
	//BeanDefinition的别名
	@Nullable
	private final String[] aliases;


	// 根据bean的名称和beanDefinition初始化BeanDefinitionHolder
	public BeanDefinitionHolder(BeanDefinition beanDefinition, String beanName) {
		this(beanDefinition, beanName, null);
	}

	// 根据bean的名称和beanDefinition,别名aliases初始化BeanDefinitionHolder
	public BeanDefinitionHolder(BeanDefinition beanDefinition, String beanName, @Nullable String[] aliases) {
		Assert.notNull(beanDefinition, "BeanDefinition must not be null");
		Assert.notNull(beanName, "Bean name must not be null");
		this.beanDefinition = beanDefinition;
		this.beanName = beanName;
		this.aliases = aliases;
	}

	// 根据指定的BeanDefinitionHolder 复制一个新的BeanDefinitionHolder
	// 浅克隆
	public BeanDefinitionHolder(BeanDefinitionHolder beanDefinitionHolder) {
		Assert.notNull(beanDefinitionHolder, "BeanDefinitionHolder must not be null");
		this.beanDefinition = beanDefinitionHolder.getBeanDefinition();
		this.beanName = beanDefinitionHolder.getBeanName();
		this.aliases = beanDefinitionHolder.getAliases();
	}


	// 获取BeanDefinition
	public BeanDefinition getBeanDefinition() {
		return this.beanDefinition;
	}

	// 获取bean的名称
	public String getBeanName() {
		return this.beanName;
	}

	// 获取别名
	@Nullable
	public String[] getAliases() {
		return this.aliases;
	}

	// 获取beanDefinition的源对象,实现了BeanMetadataElement
	@Override
	@Nullable
	public Object getSource() {
		return this.beanDefinition.getSource();
	}

	// 判断指定的名称与beanName或者别名是否匹配
	public boolean matchesName(@Nullable String candidateName) {
		return (candidateName != null && (candidateName.equals(this.beanName) ||
				candidateName.equals(BeanFactoryUtils.transformedBeanName(this.beanName)) ||
				ObjectUtils.containsElement(this.aliases, candidateName)));
	}


	//返回一个描述包括bean的名称和所有的别名
	public String getShortDescription() {
		if (this.aliases == null) {
			return "Bean definition with name '" + this.beanName + "'";
		}
		return "Bean definition with name '" + this.beanName + "' and aliases [" + StringUtils.arrayToCommaDelimitedString(this.aliases) + ']';
	}

	//返回一个长描述包括名称,别名已经beanDefinition的内容
	public String getLongDescription() {
		return getShortDescription() + ": " + this.beanDefinition;
	}

	@Override
	public String toString() {
		return getLongDescription();
	}


	@Override
	public boolean equals(@Nullable Object other) {
		if (this == other) {
			return true;
		}
		if (!(other instanceof BeanDefinitionHolder)) {
			return false;
		}
		BeanDefinitionHolder otherHolder = (BeanDefinitionHolder) other;
		return this.beanDefinition.equals(otherHolder.beanDefinition) &&
				this.beanName.equals(otherHolder.beanName) &&
				ObjectUtils.nullSafeEquals(this.aliases, otherHolder.aliases);
	}

	@Override
	public int hashCode() {
		int hashCode = this.beanDefinition.hashCode();
		hashCode = 29 * hashCode + this.beanName.hashCode();
		hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.aliases);
		return hashCode;
	}
}

 


Pt2.8 Resource

在ResourceLoader部分有介绍。

 


Pt2.9 BeanDefinitionParserDelegate

在将XML配置转为Document文档后,Spring是委托给BeanDefinitionParserDelegate来完成文档的解析的。

BeanDefinitionParserDelegate中方法比较多,都是针对xml配置中各种标签进行解析操作。

源码比较多,这里就不贴了。

 


Pt3 IoC容器初始化

了解了Spring IoC容器的核心类之后,我们正式来研究Spring IoC源码初始化流程。

 


Pt3.1 寻找入口

Spring IoC容器初始化,有两个不同的入口,我们分别介绍一下。

  • DispatcherServlet:在使用tomcat等Web容器启动时触发,继而会在init()中触发IoC初始化,配置在web.xml中;

  • ApplicationContext:在使用main()启动的Web容器启动时触发,在具体的ApplicationContext实现类中完成IoC初始化;

 

(1) DispatcherServlet#init()

在基于Web容器启动应用时,我们常见的web.xml配置如下:

<servlet>
    <servlet-name>springMVC</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:application.properties</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    <async-supported>true</async-supported>
</servlet>
<servlet-mapping>
    <servlet-name>springMVC</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

可以看到,在Web项目启动时,会执行DispatcherServlet逻辑,Spring IoC的初始化就是从DispatcherServlet.init()开始的。

 

但是,在DispatcherServlet中没有找到init()方法,最终我们在父类HttpServletBean中找到了init方法。

// org.springframework.web.servlet.HttpServletBean#init
@Override
public final void init() throws ServletException {

    // 从Web.xml配置中获取初始化参数中的bean properties配置。
    PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
    if (!pvs.isEmpty()) {
        try {
            // 定位资源
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
            // 加载配置信息
            ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
            bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
            initBeanWrapper(bw);
            bw.setPropertyValues(pvs, true);
        }
        catch (BeansException ex) {
            if (logger.isErrorEnabled()) {
                logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
            }
            throw ex;
        }
    }

    // 真正完成容器初始化动作是在initServletBean()中,由子类根据自身逻辑完成初始化(重要)。
    initServletBean();
}

 

继续来看initServletBean的逻辑:

// org.springframework.web.servlet.FrameworkServlet#initServletBean
@Override
protected final void initServletBean() throws ServletException {
    getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
    if (logger.isInfoEnabled()) {
        logger.info("Initializing Servlet '" + getServletName() + "'");
    }
    long startTime = System.currentTimeMillis();

    try {
        // 真正完成IoC容器初始化逻辑实在ApplicationContext初始化过程中(重要)。
        // initWebApplicationContext()看起来和我们手写Spring实现时,new ApplicationContext()的逻辑很像。
        this.webApplicationContext = initWebApplicationContext();
        initFrameworkServlet();
    }
    catch (ServletException | RuntimeException ex) {
        logger.error("Context initialization failed", ex);
        throw ex;
    }

    if (logger.isDebugEnabled()) {
        String value = this.enableLoggingRequestDetails ?
            "shown which may lead to unsafe logging of potentially sensitive data" :
        "masked to prevent unsafe logging of potentially sensitive data";
        logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
                     "': request parameters and headers will be " + value);
    }

    if (logger.isInfoEnabled()) {
        logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
    }
}

 

从名字来看,initWebApplicationContext就比较符合我们的思维了,我们继续看看是如何初始化ApplicationContext的。

// org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext
protected WebApplicationContext initWebApplicationContext() {
    // 1、从ServletContext中获得父容器WebApplicationContext。
    // Web的本质是Servlet实现,ApplicationContext是为了兼容Servlet逻辑,而又能够完成丰富的Web应用程序而诞生的。
    // 所以从ServletContext中获得Web中上下文信息。
    WebApplicationContext rootContext =
        WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    // 子容器
    WebApplicationContext wac = null;

    // 2、初始化时传入webApplicationContext实例对象
    // A context instance was injected at construction time -> use it
    if (this.webApplicationContext != null) {
        wac = this.webApplicationContext;
        if (wac instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
            if (!cwac.isActive()) {
                // The context has not yet been refreshed -> provide services such as
                // setting the parent context, setting the application context id, etc
                if (cwac.getParent() == null) {
                    // The context instance was injected without an explicit parent -> set
                    // the root application context (if any; may be null) as the parent
                    cwac.setParent(rootContext);
                }
                // 触发IoC容器初始化。
                configureAndRefreshWebApplicationContext(cwac);
            }
        }
    }

    // 3、从ServletContext中查找Web容器是否存在,并创建默认的空IoC容器。
    if (wac == null) {
        // No context instance was injected at construction time -> see if one
        // has been registered in the servlet context. If one exists, it is assumed
        // that the parent context (if any) has already been set and that the
        // user has performed any initialization such as setting the context id
        wac = findWebApplicationContext();
    }

    // 4、创建WebApplicationContext容器,初始化IoC容器(重要)。
    // 基于ServletContext中创建WebApplicationContext,并启动IoC容器的加载。
    if (wac == null) {
        // No context instance is defined for this servlet -> create a local one
        wac = createWebApplicationContext(rootContext);
    }

    // 5、触发onRefresh(),初始化Spring MVC的九大组件。
    if (!this.refreshEventReceived) {
        // Either the context is not a ConfigurableApplicationContext with refresh
        // support or the context injected at construction time had already been
        // refreshed -> trigger initial onRefresh manually here.
        synchronized (this.onRefreshMonitor) {
            onRefresh(wac);
        }
    }

    // 6、将WebApplicationContext对象放入ServletContext中。
    // 对应上面步骤[3.]的读取操作,只有WebApplicationContext已经完成加载,才会放入ServletContext中。
    // 所以如果ServletContext可以获取到wac,是可以直接使用的(参照步骤[3.]),否则需要重新创建(参照步骤[4.]和[5.])。
    if (this.publishContext) {
        // Publish the context as a servlet context attribute.
        String attrName = getServletContextAttributeName();
        getServletContext().setAttribute(attrName, wac);
    }

    return wac;
}
// org.springframework.web.servlet.FrameworkServlet#findWebApplicationContext
@Nullable
protected WebApplicationContext findWebApplicationContext() {
    String attrName = getContextAttribute();
    if (attrName == null) {
        return null;
    }
    WebApplicationContext wac =
        WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);
    if (wac == null) {
        throw new IllegalStateException("No WebApplicationContext found: initializer not registered?");
    }
    return wac;
}

 

执行创建ApplicationContext操作:

// org.springframework.web.servlet.FrameworkServlet#createWebApplicationContext(org.springframework.web.context.WebApplicationContext)
protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {
    return createWebApplicationContext((ApplicationContext) parent);
}

// org.springframework.web.servlet.FrameworkServlet#createWebApplicationContext(org.springframework.context.ApplicationContext)
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
    Class<?> contextClass = getContextClass();
    if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
        throw new ApplicationContextException(
            "Fatal initialization error in servlet with name '" + getServletName() +
            "': custom WebApplicationContext class [" + contextClass.getName() +
            "] is not of type ConfigurableWebApplicationContext");
    }

    // 通过反射创建新的WebApplicationContext对象
    ConfigurableWebApplicationContext wac =
        (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

    // 环境变量设置
    wac.setEnvironment(getEnvironment());
    // 父容器
    wac.setParent(parent);
    // 配置文件路径
    String configLocation = getContextConfigLocation();
    if (configLocation != null) {
        wac.setConfigLocation(configLocation);
    }

    // (重要)
    configureAndRefreshWebApplicationContext(wac);

    return wac;
}

 

configureAndRefreshWebApplicationContext是我们最终要找到的地方,他启动了Spring IoC的初始化操作:

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
    if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
        // The application context id is still set to its original default value
        // -> assign a more useful id based on available information
        if (this.contextId != null) {
            wac.setId(this.contextId);
        }
        else {
            // Generate default id...
            wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                      ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
        }
    }

    wac.setServletContext(getServletContext());
    wac.setServletConfig(getServletConfig());
    wac.setNamespace(getNamespace());
    wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

    // The wac environment's #initPropertySources will be called in any case when the context
    // is refreshed; do it eagerly here to ensure servlet property sources are in place for
    // use in any post-processing or initialization that occurs below prior to #refresh
    ConfigurableEnvironment env = wac.getEnvironment();
    if (env instanceof ConfigurableWebEnvironment) {
        ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
    }

    postProcessWebApplicationContext(wac);
    applyInitializers(wac);

    // Spring IoC容器启动入口(重要)。
    wac.refresh();
}

 

wac.refresh()是执行Spring IoC初始化的入口,容器的完整初始化逻辑实在refresh()中完成。

这条路我们不再继续分析下去,下面这种方式最终也是走到refresh()的逻辑,我们在下面这条链路再分析refresh()的具体逻辑。

 

(2) ApplicationContext入口

还有一种比较常用的启动xml配置的Web应用的方式,是通过main()方法启动ClassPathXMLApplicationContext。

// main()启动Web容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");

 

先看ClassPathXmlApplicationContext的配置:

public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
    this(new String[] {configLocation}, true, null);
}

 

实际执行逻辑的构造器

/**
 * Spring启动是需要做几件事情:
 * 1. 读取Spring配置;
 * 2. 扫描Bean定义;
 * 3. 初始化IoC,完成实例化Bean;
 * 4. DI,设置Bean的依赖关系;
 * 5. 设置Handler Mapping,配置URL和Method对应关系;
 */
public ClassPathXmlApplicationContext(
    String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
    throws BeansException {

    // 1、调用父类构造方法设置Bean资源加载器。
    super(parent);

    // 2、保存Spring配置文件的路径地址。
    // 配置文件中包含Spring默认配置,Bean定义,类自动扫描路径等,用于后续IoC、DI等。
    setConfigLocations(configLocations);

    // 3、至此,Spring IoC容器在初始化时将配置的Bean信息定位为Spring封装的Resource。

    // 4、启动IoC容器,载入Bean配置资源。
    if (refresh) {
        refresh();
    }
}

除了ClassPathXmlApplicationContext之外,Spring还有AnnotationConfigApplicationContext、FileSystemXMLApplicationContext、XmlWebApplicationContext等,他们都是AbstractApplicationContext的实现类,最终都是执行refresh()来完成IoC容器初始化。

接下来我们沿着ClassPathXmlApplicationContext这条线,看具体加载的过程。

 


Pt3.2 获得配置路径

super(parent)

通过父类完成资源加载起的初始化,看下源码:

// org.springframework.context.support.AbstractXmlApplicationContext#AbstractXmlApplicationContext(org.springframework.context.ApplicationContext)
public AbstractXmlApplicationContext(@Nullable ApplicationContext parent) {
    super(parent);
}

// org.springframework.context.support.AbstractRefreshableConfigApplicationContext#AbstractRefreshableConfigApplicationContext(org.springframework.context.ApplicationContext)
public AbstractRefreshableConfigApplicationContext(@Nullable ApplicationContext parent) {
    super(parent);
}

// org.springframework.context.support.AbstractRefreshableApplicationContext#AbstractRefreshableApplicationContext(org.springframework.context.ApplicationContext)
public AbstractRefreshableApplicationContext(@Nullable ApplicationContext parent) {
    super(parent);
}

 

一路找上去,最后在父类AbstractApplicationContext找到实现逻辑:

public AbstractApplicationContext(@Nullable ApplicationContext parent) {
    // 1、初始化Bean资源加载器;
    this();

    // 2、设置父上下文,主要是获取父上下文的配置信息。
    setParent(parent);
}

// this()的逻辑
public AbstractApplicationContext() {
    // ResourcePatternResolver继承了ResourceLoader,用于解析Bean定义的配置资源。
    // ResourceLoader接口是Spring为了统一读取诸如本地文件、classpath项目路径下的文件、url互联网上
    // 的文件等不同类型渠道的资源,封装隐藏如打开流、关闭流、报错处理等大量重复模板代码,而专程设计提供
    // 的接口类。
    this.resourcePatternResolver = getResourcePatternResolver();
}

// 获取Spring Source的加载器用于读入Spring配置信息
protected ResourcePatternResolver getResourcePatternResolver() {
    // this -> AbstractApplicationContext继承自DefaultResourceLoader,本身也是资源加载器
    // Spring资源加载器的getResource()用于载入资源
    return new PathMatchingResourcePatternResolver(this);
}

在ApplicationContext上下文中,缓存了用于将特定正则模式的资源位置路径解析为资源的解析器,ResourcePatternResolver。

在上面ApplicationContext的类图中,可以看到ApplicationContext本身是继承了ResourcePatternResolver和ResourceLoader的,所以ApplicationContext本身其实就是一个资源解析器。

 

PathMatchingResourcePatternResolver能够将指定的资源位置路径解析为一个或多个匹配的资源,这个我们在前面ResourceLoader介绍过,这里就不说明了。

// org.springframework.core.io.support.PathMatchingResourcePatternResolver#PathMatchingResourcePatternResolver(org.springframework.core.io.ResourceLoader)
public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
    Assert.notNull(resourceLoader, "ResourceLoader must not be null");
    this.resourceLoader = resourceLoader;
}

 

ApplicationContext需要操作资源解析器,继承ResourcePatternResolver,获得了资源操作的能力(继承接口方法)。

虽然本身ApplicationContext也是ResourcePatternResolver的实现类,他实现了接口的方法。但是他是通过组合的方式,调用其他ResourcePatternResolver实现类来操作资源解析,而没有自己去实现一个资源解析的具体逻辑。

比如我们看他的继承方法的实现逻辑:

@Override
public Resource[] getResources(String locationPattern) throws IOException {
    return this.resourcePatternResolver.getResources(locationPattern);
}

ApplicationContext这么设计的原因,我想应该是为了简化ApplicationContext中这块操作逻辑复杂度。

ResourceLoader是一种策略模式的设计,针对不同的资源类型有不同的实现类。比如PathMatchingResourcePatternResolver、ServletContextResourcePatternResolver等,针对不同的类型通过不同的策略类来实现逻辑,再通过组合的方式将能力集成到ApplicationContext中。这种通过继承+组合的方式,既得到了接口的能力,又简化自身代码实现逻辑,非常值得学习。

 

setConfigLocations(configLocations)

我们看看setConfigLocations的实现逻辑:

/**
 * 解析Bean定义资源文件的路径,处理多个资源文件字符串数组String[]。
 * 设置当前上下文涉及的配置文件路径,XML文件中配置了诸如Spring的Bean定义,自动扫描的basePackage等,关于Spring上下文环境信息.
 */
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++) {
            // resolvePath:将字符串解析为路径
            this.configLocations[i] = resolvePath(locations[i]).trim();
        }
    }
    else {
        this.configLocations = null;
    }
}

就是缓存构造器中传入的资源路径。

到这一步,已经完成了对Bean定义资源的定位处理。

 


Pt3.3 开始启动容器

Spring IoC容器对资源的载入是从refresh()开始的,前面也介绍过,DispatcherServlet的启动模式,最终也是通过refresh()来完成容器初始化,接下来重点研究refresh逻辑。

refresh()是一个模板方法,规定了IoC容器的启动流程,有些逻辑会交给子类来实现。

源码如下:

// org.springframework.context.support.AbstractApplicationContext#refresh
/**
 * Spring IoC容器对Bean资源配置的载入就是从refresh()开始的。refresh()是一个模板方法,规定了IoC容器的启动流程,
 */
@Override
public void refresh() throws BeansException, IllegalStateException {
    // Spring IoC加载时防止出现并发
    synchronized (this.startupShutdownMonitor) {
        StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

        // 1、调用容器准备刷新的方法,获取容器的当前时间,同时给容器设置同步标识。
        prepareRefresh();

        // 2、初始化BeanFactory,解析Spring XML配置文件,获取Bean定义(重要)。
        // 告诉子类启动refreshBeanFactory(),开始执行Bean定义资源文件载入。
        // 到这步实际上已经完成IoC容器的初始化。
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // 3、为BeanFactory配置容器特性,例如类加载器、事件处理器等。
        prepareBeanFactory(beanFactory);

        try {
            // 4、为容器的某些子类指定特殊的Post事件处理器。
            postProcessBeanFactory(beanFactory);

            StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");

            // 5、调用所有注册的BeanFactoryPostProcessor的Bean。
            invokeBeanFactoryPostProcessors(beanFactory);

            // 6、为BeanFactory注册Post事件处理器。
            registerBeanPostProcessors(beanFactory);
            beanPostProcess.end();

            // 7、初始化信息源,和国际化相关。
            initMessageSource();

            // 8、初始化容器事件传播器。
            initApplicationEventMulticaster();

            // 9、调用子类某些特殊Bean的初始化方法。
            onRefresh();

            // 10、为事件传播器注册监听事件监听器。
            registerListeners();

            // 11、初始化所有剩余的单例Bean
            finishBeanFactoryInitialization(beanFactory);

            // 12、初始化容器的生命周期事件处理器,并发布容器的生命周期事件。
            finishRefresh();
        }

        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
            }

            // 13、销毁已创建的Bean
            destroyBeans();

            // 14、取消刷新操作,重置容器的同步标识
            cancelRefresh(ex);

            // Propagate exception to caller.
            throw ex;
        }

        finally {
            // 15、重设公共缓存
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
            contextRefresh.end();
        }
    }
}

refresh()的主要作用:在创建IoC容器前,如果已经有容器存在,需要把已有的容器销毁和关闭,以保证在refresh()之后使用的是新创建的IoC容器。它类似于对IoC容器的重启,在新创建的容器中对容器进行初始化,对Bean配置资源进行载入。

Spring IoC容器对Bean载入的核心处理逻辑是在obtainFreshBeanFactory()中完成的,所以我们重点看obtainFreshBeanFactory()的逻辑。

 


Pt3.4 创建容器

进入obtainFreshBeanFactory():

// org.springframework.context.support.AbstractApplicationContext#obtainFreshBeanFactory
// 调用子类容器的refreshBeanFactory()来启动容器载入Bean配置信息的过程。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    // 1、初始化BeanFactory,解析Spring配置文件,获取Bean定义信息。
    // refreshBeanFactory()在AbstractApplicationContext中是抽象方法,未定义实现。可以看到有两个子类GenericApplicationContext和
    // AbstractRefreshableApplicationContext实现了这个方法,但是因为我们是在ClassPathXMLApplicationContext这条继承关系上,所以应该是看
    // AbstractRefreshableApplicationContext实现类的逻辑。
    refreshBeanFactory();

    // 2、返回BeanFactory,缓存了Spring IoC容器。
    return getBeanFactory();
}

 

子类实现了refreshBeanFactory():

// org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory
protected final void refreshBeanFactory() throws BeansException {
    // 1、如果BeanFactory容器已经存在,销毁容器中的Bean,关闭容器(重新加载)。
    if (hasBeanFactory()) {
        // 销毁Singleton Beans。
        // 在会面如果IoC初始化发生异常,也需要销毁Singleton Beans。其实也比较容易理解,因为Singleton只会初始化一次,一旦容器中有
        //  Singleton实例,会重复被使用,所以需要显示的进行销毁。而Prototype则不存在这类问题。
        destroyBeans();
        // 清空当前BeanFactory
        closeBeanFactory();
    }

    try {
        // 2、创建新

以上是关于07. Spring IoC源码解析的主要内容,如果未能解决你的问题,请参考以下文章

spring源码解析之IOC容器——依赖注入

转spring源码解析

spring源码解析之IOC容器------加载和注册

spring源码解析之IOC容器

spring源码深度解析— IOC 之 开启 bean 的加载

Spring IOC源码解析