Spring IOC_容器设计原理
从顶层视图看:
本质上的工作原理就是:把应用程序的类和配置元数据组装起来,以便在ApplicationContext创建并初始化好之后,IoC容器直接为你提供了一个已经配置好并且可执行的系统或应用。
BeanFactory和ApplicationContext
IOC容器接口设计图
其中的BeanFactory定义了基本的IoC容器的规范
BeanFactory容器设计原理
以XmlBeanFactory的实现为例来说明简单IoC容器的设计原理:
- BeanFactory实现是IoC容器的基本形式,各种ApplicationContext的实现是IoC容器的高级表现形式。
- DefaultListableBeanFactory作为一个默认的功能完整的IoC容器来使用;
- Resource是Spring用来封装I/O操作的类;
编程式使用IOC容器
ClassPathResource res = new ClassPathResource("spring.xml"); DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); reader.loadBeanDefinitions(res); SportService sportService = factory.getBean("runSportService1", SportService.class); sportService.doExercise();
ApplicationContext
ApplicationContext是一个高级形态意义的IoC容器:
- 支持不同的信息源:继承接口Messagesource;
- 访问资源:继承接口ResourceLoader;
- 支持应用事件
- 在ApplicationContext中提供附加功能;
设计原理
以FileSystemXmlApplicationContext为例子来说明ApplicationContext的设计原理。
AbstractRefreshableApplicationContext
ApplicationContext的主要功能在基类AbstractRefreshableApplicationContext中实现了,来看看FileSystemXmlApplicationContext的构造方法:
public FileSystemXmlApplicationContext( String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException { super(parent); setConfigLocations(configLocations); if (refresh) { refresh(); } }
ConfigurableApplicationContext
其中的refresh()方法定义在ConfigurableApplicationContext接口中。
ConfigurableApplicationContext接口主要封装配置和生命周期相关方法,这些方法几乎只用在容器启动和关闭的代码中。
ApplicationContext
根据上面的UML图可以知道,ConfigurableApplicationContext的父接口ApplicationContext:
- 继承ListableBeanFactory用于提供访问应用组件的方法;
- 继承ResourceLoader用于提供加载资源文件的方法;
- 继承ApplicationEventPublisher用于提供发布事件与注册监听器的方法;
- 继承MessageSource提供国际化的支持;
AbstractApplicationContext
其中refresh()方法的具体实现在AbstractApplicationContext抽象类中,这个refresh()方法会涉及到IoC容器启动的一系列负载操作,针对不同的容器实现,这些操作都是类似的。
不同的ApplicationContext对应不同的BeanDefinition读取方式,而FileSystemXmlApplicationContext类中提供类一个这样的方法实现:
protected Resource getResourceByPath(String path) { if (path.startsWith("/")) { path = path.substring(1); } return new FileSystemResource(path); }
调用这个方法,可以获取到FileSystemResource。
IoC容器的初始化
其中IoC容器的初始化过程,主要就体现在AbstractApplicationContext的refresh()方法中,下面是该方法关键代码:
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 1. 为刷新流程准备好context prepareRefresh(); // 2. 加载bean definitions配置并解析注册到BeanFactory中 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 3. 准备context中的BeanFactory prepareBeanFactory(beanFactory); try { // 4. 设置BeanFactory的后置处理器 postProcessBeanFactory(beanFactory); // 5. 调用BeanFactory的后置处理器,这些处理器也是context中的beans invokeBeanFactoryPostProcessors(beanFactory); // 6. 注册Bean的后置处理器,这些处理器在 bean创建的过程中被调用 registerBeanPostProcessors(beanFactory); // 7. 对上下文中的消息源进行初始化 initMessageSource(); // 8. 初始化context中的事件机制 initApplicationEventMulticaster(); // 9. 初始化其他特殊的Context中的特殊的bean onRefresh(); // 10. 检查监听beans,并且向容器注册它们 registerListeners(); // 11. 实例化剩下的所有的(non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // 12. 最后一步:发布容器事件,结束Refresh过程 finishRefresh(); } catch (BeansException ex) { // 13. 为防止Bean资源占用,销毁已经在前面过程中已经生成的单间Bean destroyBeans(); // 14. 重置'active'标志 cancelRefresh(ex); // Propagate exception to caller. throw ex; } } }
各个方法功能说明如下:
1、为刷新流程准备好Context
设置启动日期,记录容器标识位,初始化容器变量。
2、加载Bean Definition配置并解析注册到BeanFactory中
这里调用具体的子类refreshBeanFactory()方法实现来刷新内部的BeanFactory。
这一步是Bean定义信息载入的环节。最开始会创建一个DefaultListableBeanFactory,保存在ApplicationContext的beanFactory中。这一步的主要的方法是:loadBeanDefinitions(),下面来看看AbstractXMLApplicationContext中该方法的调用层次:
loadBeanDefinitions方法中创建了一个XmlBeanDefinitionReader来读取xml的配置并解析为bean definitions:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. initBeanDefinitionReader(beanDefinitionReader); loadBeanDefinitions(beanDefinitionReader); }
其中loadBeanDefinitions最终会调用到DefaultListableBeanFactory.registerBeanDefinition()方法注册bean definitions。所谓的注册,就是把解析到的BeanDefinition放入到DefaultListableBeanFactory中定义的一个beanDefinitionMap中。调用关系如下:
容器的作用就是对beanDefinitionMap里面的信息进行处理和维护。有了这些数据,就可以进行依赖注入了
3、准备Context中的BeanFactory
在使用ApplicationContext时需要做一些准备工作,这些准备工作是在这一步处理的,包括:为容器配置ClassLoader、PropertyEditor和BeanPost-Processor等,从而为容器的启动做好必要的准备。
4、允许Context的子类对BeanFactory进行后处理
postProcessBeanFactory后处理beanFactory。该方法是在所有的beanDenifition加载完成之后,bean实例化之前执行。为了能够修改bean definitions,或者对BeanFactory做一些其他配置,可以使用这个方法。如可以实现ClassPathXmlApplicationContext类并重新该方法即可。Spring中很多ApplicationContext的子类都是通过重写这个方法来达到这个目的(AbstractApplicationContext中的实现为空)。
5、调用BeanFactory的后置处理器,这些处理器也是Context中的beans
拿到当前应用上下文 beanFactoryPostProcessors 变量中的值,实例化并调用所有已注册的 BeanFactoryPostProcessor。必须在singleton之前调用。
6、注册bean的后置处理器,这些处理器在bean创建的过程中被调用
实例化并且注册所有的BeanPostProcessor beans,必须在任何bean实例化之前调用。
7、对上下文中的消息源进行初始化
初始化消息源,支持消息的参数化和国际化。
8、初始化Context中的事件机制
初始化应用事件广播器,这里使用了事件驱动机制。如果自定义了广播器,就用自对应的,否则用默认的。然后把该广播器设置到context的applicationEventMulticaster属性中。
9、初始化其他特殊的Context中的特殊的bean
特殊的context子类中初始化其他特殊的bean,使得子类在实例化单例之前,调用初始化bean,默认是空实现。
10、检查监听器,并向容器注册它们
注册监听器,在发布事件的时候会从这里注册的监听器中获取。
11、实例化剩下的所有的非懒加载的单例
这里的代码比较多,我们来看看关键代码:
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()) { ... getBean(beanName); ... // Trigger post-initialization callback for all applicable beans... for (String beanName : beanNames) { Object singletonInstance = getSingleton(beanName); if (singletonInstance instanceof SmartInitializingSingleton) { final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance; ... smartSingleton.afterSingletonsInstantiated(); ... }
这里会调用getBean(beanName)触发bean的实例化。
如果bean实现了SmartInitializingSingleton接口,则会执行该接口的afterSingletonsInstantiated方法,该方法是在所有的 单例bean创建完成之后(依赖注入完成,BeanPostProcessor,InitializingBean, initMethod相关方法执行完成后)触发执行,对于懒加载的单例bean无效。
这个接口的功能类似于ContextRefreshedEvent,都是在容器加载完成之后处理其他的事情,但是无需实现ApplicationListener,使用更佳简单。
12、最后一步:发布容器事件,结束REFRESH过程
完成容器刷新,调用LifecycleProcessor的onRefresh方法,发布ContextRefreshedEvent事件。
LifecycleProcessor:负责管理ApplicationContext的生命周期。
IoC容器的依赖注入
在没有配置lazy-init=false的情况下,依赖注入的过程是用户第一次向IoC容器索要Bean的时候出发的。具体的触发方法:
BeanFactory.getBean(String name)
来看看这个方法的调用关系:
可以发现,最终是由createBean方法执行。createBean方法生成需要的bean,并且对Bean初始化进行了处理(init-method或者Bean后置处理器等)。
其中createBean方法的RootBeanDefinition对象是在AbstractBeanFactory的doGetBean方法里面生成的:
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
如果需要获取的bean是一个子bean定义,那么会遍历父bean的定义,返回一个合并RootBeanDefinition。这个方法会递归一直往上寻找父bean,构造包含继承关系的RootBeanDefinition:
> pbd = getMergedBeanDefinition(parentBeanName); > ... > mbd = new RootBeanDefinition(pbd); > // 使用bd(可能是子bean)覆盖mbd(可能是父bean的副本),这样就把父bean和子bean的属性配置合并在一起了。 > mbd.overrideFrom(bd); >
方法中的RootBeanDefinition,如2.7、Bean定义继承,指的是父定义。在配置文件中可以定义父<bean>和子<bean>,父<bean>用RootBeanDefinition表示,而子<bean>用ChildBeanDefiniton表示,而没有父<bean>的<bean>就使用RootBeanDefinition表示。
在createBean这个方法包含以下方法调用
1、bean实例化
调用关系如下:
以下是instantiate方法:
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) { // Don't override the class with CGLIB if no overrides. if (!bd.hasMethodOverrides()) { Constructor<?> constructorToUse; ... return BeanUtils.instantiateClass(constructorToUse); } else { // Must generate CGLIB subclass. return instantiateWithMethodInjection(bd, beanName, owner); } }
可以发现,如果是接口的实现,那么就会使用CGLIB来实例化Bean,否则使用BeanUtils中的JVM反射来实例化Bean。生成的实例最终会被封装为BeanWrapper,BeanWrapper相当于一个代理器,Spring委托BeanWrapperwancehngBean属性的填充工作。在Bean实例被InstantiatioonStrategy创建出来之后,容器主控程序将Bean实例通过BeanWrapper包装起来,这是通过调用BeanWrapper#setWrappedInstance(Object obj)方法完成的。
2、bean的初始化,依赖注入一般发生在这里
createBean中调用了populateBean方法,该方法进行了依赖注入处理,主要通过bean definition中的属性值填充BeanWrapper中的bean实例,以下是关键处理代码:
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { // null实例不能处理属性依赖注入 if (bw == null) { if (mbd.hasPropertyValues()) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance"); } else { // Skip property population phase for null instance. return; } } // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the // state of the bean before properties are set. This can be used, for example, // to support styles of field injection. boolean continueWithPropertyPopulation = true; if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { continueWithPropertyPopulation = false; break; } } } } if (!continueWithPropertyPopulation) { return; } // 获取在BeanDefinition中设置的property值 PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null); int resolvedAutowireMode = mbd.getResolvedAutowireMode(); if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) { MutablePropertyValues newPvs = new MutablePropertyValues(pvs); // Add property values based on autowire by name if applicable. if (resolvedAutowireMode == AUTOWIRE_BY_NAME) { autowireByName(beanName, mbd, bw, newPvs); } // Add property values based on autowire by type if applicable. if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) { autowireByType(beanName, mbd, bw, newPvs); } pvs = newPvs; } boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors(); boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); PropertyDescriptor[] filteredPds = null; if (hasInstAwareBpps) { if (pvs == null) { pvs = mbd.getPropertyValues(); } for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { if (filteredPds == null) { filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); } pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { return; } } pvs = pvsToUse; } } } if (needsDepCheck) { if (filteredPds == null) { filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); } checkDependencies(beanName, mbd, filteredPds, pvs); } if (pvs != null) { // 对属性进行注入 applyPropertyValues(beanName, mbd, bw, pvs); } }
其中最后一个方法调用applyPropertyValues是属性注入的处理方法:
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) { ... List<PropertyValue> original; ... BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter); ... for (PropertyValue pv : original) { ... Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue); ... } ... bw.setPropertyValues(new MutablePropertyValues(deepCopy)); ... }
这里通过BeanDefinitionValueResolver来对BeanDefinition进行解析,然后注入到PropertyValues中。
主要的处理逻辑是为解析值创建一个副本:
List<PropertyValue> deepCopy = new ArrayList<>(original.size());
副本的数据最后会被注入到Bean中:
bw.setPropertyValues(new MutablePropertyValues(deepCopy));
其中resolveValueIfNecessary方法主要的作用是:
解析PropertyValue中对工厂中其他bean的任何引用。该值可以是:
- 一个BeanDefinition,它导致创建相应的新bean实例;
- 一个RuntimeBeanReference;
- 一个ManagedList。 这是一个特殊的集合,其中可能包含需要解析的RuntimeBeanReferences或Collections;
- 一个ManagedSet。 也可能包含需要解决的RuntimeBeanReferences或Collections;
- 一个ManagedMap。 在这种情况下,该值可能是需要解析的RuntimeBeanReference或Collection;
- 一个普通对象或null,在这种情况下,将不做解析。
而最后的setPropertyValues方法会为BeanWraper完成Bean的属性值注入。
总的来说,以getBean为入口,触发Bean的创建和依赖注入,其间需要依赖BeanDefinition中的信息来递归地完成依赖注入。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构