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中的信息来递归地完成依赖注入。

 

posted @   残城碎梦  阅读(125)  评论(0编辑  收藏  举报
编辑推荐:
· .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语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示