首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Spring框架中的IoC容器源码分析(上)

Spring框架中的IoC容器源码分析(上)

作者头像
老周聊架构
发布2025-11-20 10:15:09
发布2025-11-20 10:15:09
310
举报

一、Spring IoC基础

1、bean与BeanFactory的关系

2、BeanFactory与ApplicationContext区别

BeanFactorySpring框架中IoC容器的顶层接口,它只是用来定义一些基础功能,定义一些基础规范,而 ApplicationContext是它的一个子接口,所以ApplicationContext是具备BeanFactory提供的全部功能的。

通常,我们称BeanFactorySpring IoC的基础容器,ApplicationContext是容器的高级接口,比 BeanFactory要拥有更多的功能,比如说国际化支持资源访问(xmljava配置类)等等。

3、启动 IoC 容器的方式

3.1 Java环境下启动IoC容器

  • ClassPathXmlApplicationContext:从类的根路径下加载配置文件(推荐使用)
  • FileSystemXmlApplicationContext:从磁盘路径上加载配置文件
  • AnnotationConfigApplicationContext:纯注解模式下启动Spring容器

3.2 Web环境下启动IoC容器

  • 从xml启动容器
代码语言:javascript
复制
<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>

  <!--配置Spring ioc容器的配置文件-->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>
  <!--使用监听器启动Spring的IOC容器-->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
</web-app>
  • 从配置类启动容器
代码语言:javascript
复制
<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>

  <!--告诉ContextloaderListener知道我们使用注解的方式启动ioc容器-->
  <context-param>
    <param-name>contextClass</param-name>
    <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
  </context-param>
  <!--配置启动类的全限定类名-->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>com.riemann.SpringConfig</param-value>
  </context-param>
  <!--使用监听器启动Spring的IOC容器-->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
</web-app>

二、Spring IoC高级特性

1、lazy-Init 延迟加载

1.1 Bean的延迟加载(延迟创建)

ApplicationContext 容器的默认行为是在启动服务器时将所有 singleton bean 提前进行实例化。提前实例化意味着作为初始化过程的一部分,ApplicationContext 实例会创建并配置所有的singleton bean

比如:

代码语言:javascript
复制
<bean id="testBean" class="com.riemann.LazyBean" />
该bean默认的设置为:
<bean id="testBean" calss="com.riemann.LazyBean" lazy-init="false" />

lazy-init="false",立即加载,表示在spring启动时,立刻进行实例化。 如果不想让一个singleton beanApplicationContext实现初始化时被提前实例化,那么可以将bean 设置为延迟实例化。

代码语言:javascript
复制
<bean id="testBean" calss="com.riemann.LazyBean" lazy-init="true" />

设置 lazy-inittruebean 将不会在 ApplicationContext 启动时提前被实例化,而是第一次向容器 通过 getBean 索取 bean 时实例化的。

如果一个 beanscope 属性为 scope="pototype" 时,即使设置了 lazy-init="false",容器启动时也不会实例化bean,而是调用 getBean 方法实例化的。

1.2 应用场景

  • 开启延迟加载一定程度提高容器启动和运转性能
  • 对于不常使用的 Bean 设置延迟加载,这样偶尔使用的时候再加载,不必要从一开始该 Bean 就占用资源

2、FactoryBean 和 BeanFactory

BeanFactory接口是容器的顶级接口,定义了容器的一些基础行为,负责生产和管理Bean的一个工厂,具体使用它下面的子接口类型,比如ApplicationContext

此处我们重点分析FactoryBean

SpringBean有两种,一种是普通Bean,一种是工厂Bean(FactoryBean)FactoryBean可以生成 某一个类型的Bean实例(返回给我们),也就是说我们可以借助于它自定义Bean的创建过程。

Bean创建的三种方式中的静态方法实例化方法FactoryBean作用类似,FactoryBean使用较多,尤 其在Spring框架一些组件中会使用,还有其他框架和Spring框架整合时使用。

我们下面来看个例子:

FactoryBean接口:

代码语言:javascript
复制
// 可以让我们自定义Bean的创建过程(完成复杂Bean的定义) 
public interface FactoryBean<T> {

	@Nullable
	// 返回FactoryBean创建的Bean实例,如果isSingleton返回true,则该实例会放到Spring容器 的单例对象缓存池中Map
	T getObject() throws Exception;
	
	@Nullable
	// 返回FactoryBean创建的Bean类型 
	Class<?> getObjectType();
	
	// 返回作用域是否单例
	default boolean isSingleton() {
		return true;
	}
	
}

Company类:

代码语言:javascript
复制
@Data
public class Company {

    private String name;
    private String address;
    private int scale;
    
}

CompanyFactoryBean类:

代码语言:javascript
复制
public class CompanyFactoryBean implements FactoryBean<Company> {

	private String companyInfo; // 公司名称,地址,规模
	
	public void setCompanyInfo(String companyInfo) { 
		this.companyInfo = companyInfo;
	}
	
	@Override
	public Company getObject() throws Exception {
		// 模拟创建复杂对象Company
		Company company = new Company();
		String[] strings = companyInfo.split(","); 
		company.setName(strings[0]); 
		company.setAddress(strings[1]); 
		company.setScale(Integer.parseInt(strings[2]));
	    return company;
	}
	
	@Override
	public Class<?> getObjectType() { 
		return Company.class;
	}
	
	@Override
	public boolean isSingleton() {
		return true;
	} 
	
}

xml配置:

代码语言:javascript
复制
<bean id="companyBean" class="com.riemann.factory.CompanyFactoryBean"> 
	<property name="companyInfo" value="腾讯,南山科技园,60000"/>
</bean>

测试,获取FactoryBean产生的对象:

代码语言:javascript
复制
Object companyBean = applicationContext.getBean("companyBean"); System.out.println("bean:" + companyBean);
// 结果如下
bean:Company{name='腾讯', address='南山科技园', scale=60000}

测试,获取FactoryBean,需要在id之前添加“&”:

代码语言:javascript
复制
Object companyBean = applicationContext.getBean("&companyBean"); 
System.out.println("bean:" + companyBean);
// 结果如下 
bean:com.riemann.factory.CompanyFactoryBean@53f6fd09

3、后置处理器

Spring提供了两种后处理bean的扩展接口,分别为 BeanPostProcessorBeanFactoryPostProcessor,两者在使用上是有所区别的。

工厂初始化(BeanFactory)—> Bean对象

  • BeanFactory初始化之后可以使用BeanFactoryPostProcessor进行后置处理做一些事情
  • Bean对象实例化(并不是Bean的整个生命周期完成)之后可以使用BeanPostProcessor进行后置处理做一些事情

注意:对象不一定是springbean,而springbean一定是个对象

3.1 BeanPostProcessor

BeanPostProcessor是针对Bean级别的处理,可以针对某个具体的Bean。

代码语言:javascript
复制
public interface BeanPostProcessor {

	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

}

该接口提供了两个方法,分别在Bean的初始化方法前和初始化方法后执行,具体这个初始化方法指的是 什么方法,类似我们在定义bean时,定义了init-method所指定的方法。

定义一个类实现了BeanPostProcessor,默认是会对整个Spring容器中所有的bean进行处理。如果要对具体的某个bean处理,可以通过方法参数判断,两个类型参数分别为ObjectString,第一个参数是每个bean的实例,第二个参数是每个beanname或者id属性的值。所以我们可以通过第二个参数,来判断我们将要处理的具体的bean

注意:处理是发生在Spring容器的实例化和依赖注入之后

3.2 BeanFactoryPostProcessor

BeanFactory级别的处理,是针对整个Bean的工厂进行处理,典型应用:PropertyPlaceholderConfigurer

代码语言:javascript
复制
@FunctionalInterface
public interface BeanFactoryPostProcessor {

	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}

此接口只提供了一个方法,方法参数为ConfigurableListableBeanFactory,该参数类型定义了一些方法:

其中有个方法名为getBeanDefinition的方法,我们可以根据此方法,找到我们定义beanBeanDefinition对象。然后我们可以对定义的属性进行修改,以下是BeanDefinition中的方法:

方法名字类似我们bean标签的属性,setBeanClassName对应bean标签中的class属性,所以当我们拿到BeanDefinition对象时,我们可以手动修改bean标签中所定义的属性值。

BeanDefinition对象:我们在 XML 中定义的 bean标签,Spring 解析 bean 标签成为一个 JavaBean, 这个JavaBean 就是 BeanDefinition

注意:调用 BeanFactoryPostProcessor 方法时,这时候bean还没有实例化,此时 bean 刚被解析成 BeanDefinition对象。

三、Spring IOC 源码深度剖析

1、Spring IoC容器初始化主体流程

1.1 Spring IoC的容器体系

IoC容器是Spring的核心模块,是抽象了对象管理、依赖关系管理的框架解决方案。Spring 提供了很多的容器,其中 BeanFactory顶层容器(根容器),不能被实例化,它定义了所有 IoC 容器必须遵从的一套原则,具体的容器实现可以增加额外的功能,比如我们常用到的ApplicationContext,其下更具体的实现如 ClassPathXmlApplicationContext 包含了解析 xml 等一系列的内容,AnnotationConfigApplicationContext 则是包含了注解解析等一系列的内容。Spring IoC 容器继承体系非常聪明,需要使用哪个层次用哪个层次即可,不必使用功能大而全的。

BeanFactory 顶级接口方法栈如下:

BeanFactory 容器继承体系如下:

通过其接口设计,我们可以看到我们一贯使用的 ApplicationContext 除了继承BeanFactory的子接口, 还继承了ResourceLoaderMessageSource等接口,因此其提供的功能也就更丰富了。

下面我们以 ClasspathXmlApplicationContext 为例,深入源码说明 IoC 容器的初始化流程。

1.2 Bean生命周期关键时机点

思路:创建一个类 RiemannBean ,让其实现几个特殊的接口,并分别在接口实现的构造器接口方法中 断点,观察线程调用栈,分析出 Bean 对象创建和管理关键点的触发时机。

RiemannBean

代码语言:javascript
复制
public class RiemannBean implements InitializingBean, ApplicationContextAware {

	private ItBean itBean;

	public void setItBean(ItBean itBean) {
		this.itBean = itBean;
	}

	public RiemannBean() {
		System.out.println("RiemannBean 构造器...");
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		System.out.println("RiemannBean afterPropertiesSet...");
	}

	public void print() {
		System.out.println("RiemannBean print方法业务逻辑执行...");
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		System.out.println("RiemannBean setApplicationContext...");
	}

}

BeanPostProcessor 接口实现类

代码语言:javascript
复制
public class MyBeanPostProcessor implements BeanPostProcessor {

	public MyBeanPostProcessor() {
		System.out.println("BeanPostProcessor 实现类构造函数...");
	}

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		if("riemannBean".equals(beanName)) {
			System.out.println("BeanPostProcessor 实现类 postProcessBeforeInitialization 方法被调用中...");
		}
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if("riemannBean".equals(beanName)) {
			System.out.println("BeanPostProcessor 实现类 postProcessAfterInitialization 方法被调用中...");
		}
		return bean;
	}

}

BeanFactoryPostProcessor 接口实现类

代码语言:javascript
复制
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

	public MyBeanFactoryPostProcessor() {
		System.out.println("BeanFactoryPostProcessor的实现类构造函数...");
	}

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		System.out.println("BeanFactoryPostProcessor的实现方法调用中...");
	}

}

applicationContext.xml

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xsi:schemaLocation="
	    http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
">

	
	<bean id="riemannBean" class="com.riemann.RiemannBean">
		<property name="ItBean" ref="itBean"/>
	</bean>
	<bean id="myBeanFactoryPostProcessor" class="com.riemann.MyBeanFactoryPostProcessor"/>
	<bean id="myBeanPostProcessor" class="com.riemann.MyBeanPostProcessor"/>
	<!--循环依赖问题-->
	<bean id="itBean" class="com.riemann.ItBean">
		<property name="riemannBean" ref="riemannBean"/>
	</bean>

</beans>

IoC 容器源码分析用例

代码语言:javascript
复制
public class IocTest {

	/**
	 *  Ioc 容器源码分析基础案例
	 */
	@Test
	public void testIoC() {
		// ApplicationContext是容器的高级接口,BeanFacotry(顶级容器/根容器,规范了/定义了容器的基础行为)
		// Spring应用上下文,官方称之为 IoC容器(错误的认识:容器就是map而已;准确来说,map是ioc容器的一个成员,
		// 叫做单例池, singletonObjects,容器是一组组件和过程的集合,包括BeanFactory、单例池、BeanPostProcessor等以及之间的协作流程)

		/**
		 * Ioc容器创建管理Bean对象的,Spring Bean是有生命周期的
		 * 构造器执行、初始化方法执行、Bean后置处理器的before/after方法:AbstractApplicationContext#refresh#finishBeanFactoryInitialization
		 * Bean工厂后置处理器初始化、方法执行:AbstractApplicationContext#refresh#invokeBeanFactoryPostProcessors
		 * Bean后置处理器初始化:AbstractApplicationContext#refresh#registerBeanPostProcessors
		 */
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
		RiemannBean riemannBean = applicationContext.getBean(RiemannBean.class);
		System.out.println(riemannBean);
	}

}

上面注释是我已经断点调试完验证的结果

1.3 小结

根据上面的调试分析,我们发现Bean对象创建的几个关键时机点代码层级的调用都在 AbstractApplicationContext 类 的 refresh 方法中,可⻅这个方法对于Spring IoC 容器初始化来说相当关键,汇总如下:

关键点

触发代码

构造器

AbstractApplicationContext#refresh#finishBeanFactoryInitialization(beanFactory)

BeanFactoryPostProcessor 初始化

AbstractApplicationContext#refresh#invokeBeanFactoryPostProcessors(beanFactory)

BeanFactoryPostProcessor 方法调用

AbstractApplicationContext#refresh#invokeBeanFactoryPostProcessors(beanFactory)

BeanPostProcessor 初始化

AbstractApplicationContext#refresh#registerBeanPostProcessors(beanFactory)

BeanPostProcessor 方法调用

AbstractApplicationContext#refresh#finishBeanFactoryInitialization(beanFactory)

2、Spring IoC容器初始化主流程源码剖析

由上分析可知,Spring IoC 容器初始化的关键环节就在 AbstractApplicationContext#refresh() 方法中 ,我们查看 refresh 方法来俯瞰容器创建的主体流程,主体流程下的具体子流程我们后面再来讨论。

代码语言:javascript
复制
@Override
public void refresh() throws BeansException, IllegalStateException {
	/**
	 * 对象锁加锁
	 */
	synchronized (this.startupShutdownMonitor) {
		// Prepare this context for refreshing.
		/**
		 * 刷新前的预处理
		 * 表示在真正做refresh操作之前需要准备做的事情:
		 *    设置Spring容器启动时间
		 *    开启活跃状态,撤销关闭状态
		 *    验证环境信息里一些必须存在的属性
		 */
		prepareRefresh();

		// Tell the subclass to refresh the internal bean factory.
		/**
		 * 获取BeanFactory:默认实现是DefaultListableBeanFactory
		 * Bean获取并封装成BeanDefinition对象
		 * 加载BeanDefinition 并注册到BeanDefinitionRegistry
		 */
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

		// Prepare the bean factory for use in this context.
		/**
		 * 获取BeanFactory的准备工作:BeanFactory进行一些设置,比如context的类加载器等。
		 */
		prepareBeanFactory(beanFactory);

		try {
			// Allows post-processing of the bean factory in context subclasses.
			/**
			 * BeanFactory准备工作完成后进行的后置处理工作
			 */
			postProcessBeanFactory(beanFactory);

			// Invoke factory processors registered as beans in the context.
			/**
			 * 实例化实现了BeanFactoryPostProcessor接口的Bean,并调用接口方法
			 */
			invokeBeanFactoryPostProcessors(beanFactory);

			// Register bean processors that intercept bean creation.
			/**
			 * 注册BeanPostProcessor(Bean的后置处理器),在创建bean的前后等执行
			 */
			registerBeanPostProcessors(beanFactory);

			// Initialize message source for this context.
			/**
			 * 初始化MessageSource组件(做国际化功能、消息绑定、消息解析)
			 */
			initMessageSource();

			// Initialize event multicaster for this context.
			/**
			 * 初始化事件派发器
			 */
			initApplicationEventMulticaster();

			// Initialize other special beans in specific context subclasses.
			/**
			 * 子类重写这个方法,在容器刷新的时候可以自定义逻辑,如创建Tomcat、Jetty等WEB服务器
			 */
			onRefresh();

			// Check for listener beans and register them.
			/**
			 * 注册应用的监听器,就是注册实现了ApplicationListener接口的监听器bean
			 */
			registerListeners();

			// Instantiate all remaining (non-lazy-init) singletons.
			/**
			 * 初始化所有剩下的非懒加载的单例bean
			 * 初始化创建非懒加载方式的单例Bean实例(未设置属性)
			 * 填充属性
			 * 初始化方法调用(比如调用afterPropertiesSet方法、init-method方法)
			 * 调用BeanPostProcessor(Bean的后置处理器)对实例bean进行后置处理
			 */
			finishBeanFactoryInitialization(beanFactory);

			// Last step: publish corresponding event.
			/**
			 * 完成context的刷新,主要是调用LifecycleProcessor的onRefresh()方法,并且发布事件(ContextRefreshEvent)
			 */
			finishRefresh();
		}

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

			// Destroy already created singletons to avoid dangling resources.
			destroyBeans();

			// Reset 'active' flag.
			cancelRefresh(ex);

			// Propagate exception to caller.
			throw ex;
		}

		finally {
			// Reset common introspection caches in Spring's core, since we
			// might not ever need metadata for singleton beans anymore...
			resetCommonCaches();
		}
	}
}

欢迎大家关注我的公众号【老周聊架构】,AI、大数据、云原生、物联网等相关领域的技术知识分享。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-11-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 老周聊架构 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、Spring IoC基础
  • 二、Spring IoC高级特性
  • 三、Spring IOC 源码深度剖析
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档