详解Spring中IOC容器的顶层接口


思考,输出,沉淀。用通俗的语言陈述技术,让自己和他人都有所收获。

作者:毅航😜


IOC(Inverse of Control,控制反转)可以说是Spring中的核心设计理念之一,其出现的目的在于降低组件之间的耦合度,从而提高代码的可维护性和可测试性。

IOC容器作为Spring框架的核心组件之一,其负责管理应用程序中bean的对象生命周期和依赖关系。为此Spring内部提供复杂的容器架构来完成这一功能,这也就导致了Spring容器有下图所示的错综复杂的体系结构。

注:本篇重点对Spring容器中的顶层接口:HierarchicalBeanFactoryListableBeanFactoryAutowireCapableBeanFactory进行分析介绍。

面对如此复杂的结构初学者可能会感到茫然无措。如果你也有相同的疑惑,不妨跟着笔者的思路来重新梳理SpringIOC容器间关系,理清Spring中的容器体系,体会SpringIOC的设计精髓。

(注:不了解IOC相关知识的读者可参考笔者之前文章:深入探究控制反转(IOC)与依赖注入(DI)间的关系) 进行了解 )

万物之始------BeanFactory

结合一开始的UML类图不难看出,在Spring中所有的容器都会扩展于BeanFactory这个顶层接口。依据设计模式的中的单一职责原则来看:一个类或模块应该有且仅有一个引起它变化的原因

换句话说,一个类或模块应该只负责一种类型的任务或功能。这也就意味着一个类或模块应该专注于完成特定的功能,而不应该承担过多的责任。

因此BeanFactory作为IOC容器的顶层接口,其所负责实现的功能应该是最简单。明确了这点后,我们来看BeanFactory的相关源码也就会变的豁然开朗。

java 复制代码
public interface BeanFactory {

   /**
   * 获取bean信息
   **/
    Object getBean(String name) throws BeansException;
    
    // ... 省略getBean各类重载方法
    
    /**
   * 检测bean是否存在于容器
   **/
    boolean containsBean(String name);
    
    /**
   * 判断bean的类型信息
   **/
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
    boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
    
   /**
   * 获取bean的别名信息
   **/
    String[] getAliases(String name);
}

不难发现,BeanFactory接口中定义的IOC容器的基本行为包括:Bean实例获取、判断Bean是否存在、Bean别名获取等行为。总的来看,BeanFactory接口是Spring框架中IOC容器的核心接口之一,它负责管理和控制应用程序中的对象,从而定义了IOCDI的核心功能。

进一步,通过BeanFactory开发人员可以轻松地管理和维护应用程序中的各种组件,从而提高了代码的可维护性和可测试性。

枚举Bean实例------ListableBeanFactory

SpringListableBeanFactory 继承自 BeanFactory接口,因此其除了扩展了BeanFactory基本的功能外,其还提供了更强大的检索和操作Bean的功能。

换句话,ListableBeanFactory 提供了检索Bean对象更多方式,让我们在获取Bean时,不再拘泥于BeanFactroygetBean(String name)通过beanName获取Bean实例的方式,而是提供了按类型、名称模式、注解等条件批量检索 Bean的方式。其内部相关源码如下:

java 复制代码
public interface ListableBeanFactory extends BeanFactory {

  
    // 省略一些同名重载方法
   
  /**
   * 按类型获取 Bean 实例
   ***/
   String[] getBeanNamesForType(ResolvableType type);

 
    /**
   * 获取所有 Bean 的名称
   ***/
    String[] getBeanDefinitionNames();

 
    /**
   * 按注解获取 Bean 实例
   ***/
   String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType);

例如,如果想获取容器中所有类型为UserService类型的Bean的名称,代码可写成如下形式:

java 复制代码
// 构建上下文环境
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 获取工厂
ListableBeanFactory factory = (ListableBeanFactory) context;

// 调用ListableBeanFactory中的getBeanNamesForType方法
String[] serviceBeanNames = factory.getBeanNamesForType(UserService.class);

// 循环打印
for (String beanName : allBeanNames) {
    System.out.println("Bean name: " + beanName);
}

容器间的继承------HierarchicalBeanFactory

google翻译中Hierarchical一词被解释为:"分层的、等级的"的含义。而在Spring框架中,HierarchicalBeanFactory提供了一种维护Bean工厂间父子关系的机制。即每个 HierarchicalBeanFactory 可以有一个父工厂,同时也可以作为其他工厂的父工厂。其内部方法也比较简洁:

java 复制代码
public interface HierarchicalBeanFactory extends BeanFactory {

 
   @Nullable
   BeanFactory getParentBeanFactory();


   boolean containsLocalBean(String name);

}

其中,getParentBeanFactory()的作用在于获取当前容器的父BeanFactory对象;而方法 containsLocalBean(String name) 则用于检查当前本地的容器中是否有指定名称的 Bean ,而不会往上找父 BeanFactory 。该方法要与我们熟知的getBean进行区分,对于getBean而言,其会首先从当前 BeanFactory 开始查找是否存在指定的 Bean ,如果当前找不到就依次向上找父 BeanFactory 直到找到为止返回,如果找不到则会抛出 NoSuchBeanDefinitionException的相关异常。

这里不妨思考一个问题:HierarchicalBeanFactory 可以使得容器间产生继承关系,假如现在有两个容器A和B,其中容器A为B的父容器,那么容器A和容器B中是否可以有重名的Bean实例呢?欢迎评论区留下你的想法~~~

总之,HierarchicalBeanFactory 提供了一种灵活的机制来管理 bean 工厂之间的父子关系,并允许在这种分层结构中共享配置、资源和bean定义。

自动装配的基石------AutowireCapableBeanFactory

Spring中,AutowireCapableBeanFactory 提供了 Bean实例自动装配(Autowiring)的核心功能。其允许对现有的Bean实例进行自动装配,注入其依赖。

此外,它还支持调用 Bean的初始化和销毁方法,以及应用 BeanPostProcessor 进行自定义的前后处理。

java 复制代码
public interface AutowireCapableBeanFactory extends BeanFactory {

    // 省略其他内部属性即重名重载方法

    // 对现有的 Bean 实例进行自动装配。
    void autowireBean(Object existingBean)
    // 创建一个新的 Bean 实例,并自动装配其依赖。
    <T> T createBean(Class<T> beanClass)
    // 配置现有的 Bean 实例,包括自动装配和应用后置处理器。
    Object configureBean(Object existingBean, String beanName)
    //  初始化现有的 Bean 实例,包括调用初始化方法和后置处理器
    void initializeBean(Object existingBean, String beanName)
    // 销毁现有的 Bean 实例,包括调用销毁方法和后置处理器。
    void destroyBean(Object existingBean, String beanName)

} 

接下来,我们便通过一个例子来演示如何通过AutowireCapableBeanFactory来完成自动装配,开始之前我们先定义如下UserMapperUserService两个类,其依赖关系如下所示:

java 复制代码
public class UserMapper {
    public void doSomething() {
        System.out.println("Doing something in MyRepository");
    }
}

public class UserService {
    @Autowired
    private UserMapper userMapper;

    public void performAction() {
        userMapper.doSomething();
    }
}

(注:此时省略UserMapperUserServicebeans.xml的配置信息)

java 复制代码
// 加载 Spring 配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

 // 获取 AutowireCapableBeanFactory
AutowireCapableBeanFactory factory = context.getAutowireCapableBeanFactory();

// 对现有 Bean 实例进行自动装配
UserService userService = new UserService();

factory.autowireBean(existingBean);

在上述代码中,通过 autowireBean 方法,UserService 实例的 userMapper 字段将会被自动注入一个 UserMapper 实例,从而可以正确调用 performAction 方法。

总的来看,AutowireCapableBeanFactory 提供了灵活的自动装配和Bean管理功能,使你可以在运行时动态地创建和配置 Bean 实例,并自动注入它们的依赖。

总结

本文从BeanFactory入手,详细的对Spring容器中的顶层接口:HierarchicalBeanFactoryListableBeanFactoryAutowireCapableBeanFactory进行分析介绍。具体来看

  • HierarchicalBeanFactory :支持父子 Bean 工厂的层次结构,允许在父工厂中查找 Bean
  • ListableBeanFactory :提供批量检索和查询 Bean 的功能,支持按类型和注解查找 Bean
  • AutowireCapableBeanFactory :提供自动装配和Bean生命周期管理功能,支持动态创建和配置 Bean
相关推荐
尘浮生几秒前
Java项目实战II基于微信小程序的电影院买票选座系统(开发文档+数据库+源码)
java·开发语言·数据库·微信小程序·小程序·maven·intellij-idea
郑祎亦23 分钟前
Spring Boot 项目 myblog 整理
spring boot·后端·java-ee·maven·mybatis
不是二师兄的八戒23 分钟前
本地 PHP 和 Java 开发环境 Docker 化与配置开机自启
java·docker·php
爱编程的小生35 分钟前
Easyexcel(2-文件读取)
java·excel
本当迷ya36 分钟前
💖2025年不会Stream流被同事排挤了┭┮﹏┭┮(强烈建议实操)
后端·程序员
带多刺的玫瑰1 小时前
Leecode刷题C语言之统计不是特殊数字的数字数量
java·c语言·算法
计算机毕设指导62 小时前
基于 SpringBoot 的作业管理系统【附源码】
java·vue.js·spring boot·后端·mysql·spring·intellij-idea
Gu Gu Study2 小时前
枚举与lambda表达式,枚举实现单例模式为什么是安全的,lambda表达式与函数式接口的小九九~
java·开发语言
Chris _data2 小时前
二叉树oj题解析
java·数据结构
牙牙7052 小时前
Centos7安装Jenkins脚本一键部署
java·servlet·jenkins