Spring Bean对象的创建

316 阅读16分钟

一、Spring Bean 初印象

在 Spring 框架的广袤天地里,Bean 无疑是最为核心的概念之一,就如同闪耀在苍穹的启明星,照亮着整个应用开发的漫漫长路。它可不是普通的 Java 对象,而是被 Spring IoC(控制反转)容器精心雕琢、悉心呵护的 “宠儿”。从业务逻辑的处理者到数据访问的执行者,从服务的提供者到控制器的调度者,Bean 如同万能的积木,搭建起应用程序的坚固大厦,让复杂的系统架构得以有序构建。

理解 Bean 的创建过程,就像是掌握了一把开启高效开发之门的金钥匙,不仅能让我们洞悉 Spring 底层的运行机制,更能在遇到问题时,迅速定位、精准施策,让代码更加健壮、性能更加卓越。接下来,就让我们一步步揭开 Spring 创建 Bean 对象的神秘面纱。

二、创建 Bean 的详细步骤

image.png

(一)加载 Bean 信息

当 Spring 容器启动时,就如同一位经验丰富的图书管理员,会以 refresh 方法作为入口,按图索骥,将那些被 @Component、@Service、@Repository 等 IOC 注解修饰的类,或者通过 XML 配置的类,逐一扫描进来。这个过程就像是把散落的拼图碎片收集起来,为后续构建完整的应用图景做准备。这些类信息会被精心整理,形成一个个 BeanDefinition,它宛如一本详尽的说明书,涵盖了 bean 是否单例、是否有依赖(如 @DependOn 注解所标识)、bean 的名称、所属 class 的全路径等关键信息。这一步就像是为 bean 构建了 “基因库”,后续创建 bean 的每一个环节,都离不开这些元信息的指引,其重要性不言而喻,为整个 bean 的创建之旅奠定了坚实基础。

(二)实例化 Bean

在实例化阶段,容器就像一个谨慎的管家,先是小心翼翼地尝试从缓存中获取 bean。它会依次在三级缓存里寻觅,先是一级缓存 singletonObjects,这里存放的是已然完整、实例化与初始化均已完成的 bean,如同精心打磨的成品,随时可供取用;若未找到,便转向二级缓存 earlySingletonObjects,此处收纳的是实例化完成但初始化尚在途中的 “半成品” bean;要是还没有收获,最后会在三级缓存 singletonFactories 中探寻,这里存放的是能产出 bean 的工厂,是解决循环依赖的关键所在。当所有缓存都一无所获时,容器便确认当前 bean 尚待创建,随即开启一系列精细操作。

首先,依据 BeanDefinition 检查是否存在依赖的 bean,若发现配置了 @DependOn 注解,便会优先加载这些依赖 bean,确保依赖关系的有序梳理。接着判断是否为单例 bean,若是,则步入单例 bean 的专属创建流程;若否,则进一步甄别是否为原型 bean,若是原型 bean 则开启相应创建路径,否则按照其他特定规则创建。

当开始实例化时,容器会调用 getSingleton 方法,此时传入的是对象工厂(ObjectFactory)的实现类,由于对象工厂是函数式接口,实际传入的便是 createBean 的 lambda 表达式。与此同时,将当前 bean 加入到正在创建 bean 的集合中,就像给忙碌的生产线贴上一个 “进行中” 的标签。随后调用对象工厂的 getObject 方法,触发 createBean 进而调用 doCreateBean 以及 createBeanInstance,在这一系列底层操作中,Spring 运用反射技术,精准获取构造参数,将对象从无到有地创建出来。此时的对象,恰似一张刚刚勾勒出轮廓的画作,只是通过空参构造创建的初始模样,尚未填充任何属性。最后,调用 addSingletonFactory 方法,将这个实例化完成的 bean 加入到三级缓存,至此,实例化阶段暂告一段落,为后续的雕琢打磨搭好了架子。

(三)Bean 属性填充

属性填充,恰似为 bean 这个 “骨架” 添上丰满的 “血肉”,是为其自身属性赋值,赋予 bean 鲜活生命力的关键环节。根据依赖注入(DI)注解,如常见的 @Autowired、@Resource 等,容器会先尝试从三个缓存中获取所需的 bean。若缓存中不见其踪影,便会果断尝试创建 bean,这一过程如同触发了连锁反应,若是走到了 bean 的创建步骤,便会重新完整地走一遍 bean 创建的整个流程,形成一种递归逻辑,确保每个依赖都能精准满足。

populateBean 方法便是这一环节的核心 “指挥官”,作为填充属性的入口,它接收 beanName 和 BeanDefinition 作为指令。首先从 BeanDefinition 中获取属性注入相关信息,如同查阅精细的装配图纸,然后判断是依据名称注入还是类型注入。接着调用 getSingleton 从容器中搜索所需对象,一旦获取不到,就毫不犹豫地重走一遍对象创建的复杂流程。直到最终拿到完整对象,才将其妥善地给到当前 bean 的属性,就像为精密仪器精准嵌入零部件,至此,属性填充大功告成,bean 也愈发完整、实用。

(四)初始化 Bean

即便属性填充完毕,bean 的成长之路仍未走完,Spring 还有一系列精细的 “打磨” 工序要做,其中就包括对 aware 接口、postprocessor 接口、初始化接口等的妥善处理。

aware 接口家族成员众多,像 BeanNameAwareBeanFactoryAware 等,它们如同敏锐的感知触角,能让 bean 感知到自身在容器中的 “身份” 和所处环境。当 Spring 检测到 bean 实现了这些接口时,便会迅速调用其对应的实现方法,让 bean 与容器环境紧密相连,无缝对接。

接着,Spring 会在容器中搜罗所有的 postprocessor 接口实现类,这些类如同专业的质检员,会在 bean 初始化前后执行前置方法和后置方法,为 bean 的质量把关。开发者可以巧妙利用这些前置和后置方法,嵌入自定义的逻辑,就像为产品定制个性化的检测标准,不过要注意,这些方法作用范围广泛,会影响到所有 bean,使用时需谨慎规划。

此外,若 bean 实现了初始化接口 InitializingBean,Spring 同样不会忽视,会精准执行其 afterPropertiesSet 方法,确保 bean 在投入使用前完成最后的自我调整,以最佳状态迎接后续任务。通过这一系列环环相扣的操作,bean 从一个初出茅庐的新手,成长为能够独当一面的成熟组件,随时准备在应用中发挥关键作用。

(五)后置操作

后置操作,虽处于 bean 创建流程的尾声,却起着画龙点睛的作用,它就像是一位经验老到的工匠,对刚刚打造好的 bean 进行最后的精细打磨。在这一阶段,Spring 会对 bean 进行一些必要的完善,例如类型转换等操作,确保 bean 在使用时能够无缝适配各种场景。经过这一步,bean 才算是真正完成了从原材料到成品的华丽蜕变,以最完美的姿态融入到 Spring 应用的大家庭中,随时响应各种业务需求,为系统的稳定高效运行贡献力量。

三、三级缓存的神奇之处

(一)三级缓存是什么

在 Spring 的世界里,为了解决单例 bean 之间复杂的循环依赖问题,精心构建了一套精妙绝伦的三级缓存机制,就如同层层嵌套的精密机关,环环相扣、各司其职。

首先是一级缓存 singletonObjects,它宛如一座精心守护的宝库,存放的都是历经重重工序,已然完整、成熟的单例 bean,这些 bean 不仅完成了实例化,更是顺利通过属性填充、初始化以及各种后置处理的严苛考验,如同精心雕琢、打磨完毕的艺术品,随时可供取用,是 bean 使用时的首选来源。

与之相邻的二级缓存 earlySingletonObjects,则像是一个半成品的暂存区,收纳的是那些虽然已经抢先一步完成实例化,早早曝光在 Spring 容器的视野中,但仍在属性填充或初始化途中艰难跋涉的 bean。它们像是尚未完全竣工的建筑,虽已初见雏形,但仍需后续的精细打磨,不过即便如此,在特定场景下,也能解其他 bean 的燃眉之急,满足其对依赖的迫切需求。

而处于这一体系底层,却起着关键奠基作用的三级缓存 singletonFactories,它并不直接存放 bean 实例,而是别具匠心地存储着能产出 bean 的工厂对象 ObjectFactory。在必要时刻,通过调用工厂的 getObject 方法,便能如魔法召唤一般,变出所需的 bean 实例。这一层缓存之所以如此设计,背后大有深意,考虑到 BeanPostProcessor 等机制可能会对 bean 进行一些诸如代理对象生成等复杂的初始化操作,过早固定 bean 实例可能会导致后续的灵活性受限,而工厂模式则为这种动态变化预留了充足的施展空间。

(二)三级缓存的使用时机

不妨通过一个生动鲜活的示例来深入探寻三级缓存的使用时机。假设在一个学校管理系统中有两个核心的单例 bean:StudentServiceTeacherServiceStudentService 在处理学生选课等业务逻辑时,需要依赖 TeacherService 来获取授课教师的相关信息;而无巧不成书,TeacherService 在进行教师排课等操作时,同样离不开 StudentService 提供的学生选课数据,如此一来,便形成了一个典型的循环依赖场景。

当 Spring 容器着手创建 StudentService 这个 bean 时,先是有条不紊地完成实例化步骤,此时的 StudentService 就像一个刚刚搭建好框架的毛坯房,空有其形,内部属性尚未填充。紧接着,Spring 依据配置判断该 bean 允许提前曝光,于是迅速将其包装成 ObjectFactory 后存入三级缓存 singletonFactories,这一操作就像是给半成品盖上了一层带有特殊标记的保护膜,等待后续的进一步完善。

随后,在进行 StudentService 的属性填充环节,当发现依赖 TeacherService 时,Spring 会立刻开启 TeacherService 的创建流程。同样地,TeacherService 完成实例化后也进入属性填充阶段,此时它急需 StudentService 的支撑。奇妙的是,Spring 会按既定规则,依次从一级缓存、二级缓存寻觅无果后,最终在三级缓存中精准定位到 StudentService 的工厂对象,进而通过 getObject 方法巧妙获取到尚在完善中的 StudentService 实例,这一过程就像是在错综复杂的线索中找到了关键线头,成功解开了循环依赖的死结。与此同时,获取到的 StudentService 实例会从三级缓存优雅迁移至二级缓存,仿佛完成了一次成长的蜕变,标志着它离成熟可用又近了一步。

待 StudentService 和 TeacherService 都顺利走过初始化等后续流程,如同完成了最后的精装修,它们便会以完美姿态进驻一级缓存,随时准备为系统的稳定高效运行贡献力量,至此,三级缓存圆满完成了它在解决循环依赖这场 “大戏” 中的关键使命,确保整个系统有序运转。

四、自定义实现操作大揭秘

(一)可自定义的环节

在 Spring 创建 bean 的漫漫流程中,其实暗藏着诸多可自定义实现的精妙环节,宛如一扇扇通往个性化世界的秘密之门,等待开发者去开启。

首当其冲的便是 BeanDefinitionRegistryPostProcessor,它宛如一位神通广大的 “bean 定义管理员”,继承自 BeanFactoryPostProcessor,并额外拥有一个 “魔法技能”——postProcessBeanDefinitionRegistry 方法。在 Spring 容器初始化,bean 定义信息加载完成,而实例化尚未来临的这个关键 “窗口期”,它得以大显身手,能够随心所欲地对 BeanDefinitionRegistry 进行操作,无论是新增、修改还是删除 bean 定义,都在它的掌控之中,为后续 bean 的创建路径埋下伏笔。

@Configuration
public class CustomBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        // 新增一个Bean定义示例,这里定义一个简单的名为"customBean"的Bean
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(CustomBean.class);
        registry.registerBeanDefinition("customBean", beanDefinition);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 这里如果有需要对BeanFactory进行额外处理可以添加代码,当前示例暂不做额外处理
    }
}

class CustomBean {
    // 这里可以定义CustomBean的相关属性和方法等
    private String message = "Hello from CustomBean";

    public String getMessage() {
        return message;
    }
}

在上述代码中:

  • 定义了CustomBeanDefinitionRegistryPostProcessor类实现BeanDefinitionRegistryPostProcessor接口。
  • postProcessBeanDefinitionRegistry方法中,创建了一个GenericBeanDefinition,并将其注册到BeanDefinitionRegistry中,相当于新增了一个名为"customBean"Bean定义,后续 Spring 容器就可以根据这个定义去实例化对应的Bean

与之并肩作战的 BeanFactoryPostProcessor,则像是一位精细的 “工厂调整师”,在 BeanDefinition 已然确定,bean 实例化启动之前,获得施展才华的机会。它可以通过 postProcessBeanFactory 方法,深入 ConfigurableListableBeanFactory 内部,对 bean 的定义进行精细调整,比如修改属性值、注入额外依赖,为 bean 的初始化营造最佳环境。

@Configuration
public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 假设存在一个名为"existingBean"的Bean定义,这里修改它的某个属性(示例中简单修改属性值)
        if (beanFactory.containsBean("existingBean")) {
            GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanFactory.getBeanDefinition("existingBean");
            beanDefinition.getPropertyValues().add("newProperty", "newValue");
        }
    }
}

class ExistingBean {
    private String property;

    public String getProperty() {
        return property;
    }

    public void setProperty(String property) {
        this.property = property;
    }
}

在这个示例中:

  • CustomBeanFactoryPostProcessor实现了BeanFactoryPostProcessor接口,重写了postProcessBeanFactory方法。
  • 代码中判断如果BeanFactory中存在名为"existingBean"Bean定义,就获取其对应的GenericBeanDefinition,然后往其属性值中添加一个新的属性(这里只是简单示意修改属性相关操作),以此来对Bean的定义进行调整。

再看 BeanPostProcessor,这位 “bean 加工大师” 聚焦于 bean 的初始化阶段,拥有 postProcessBeforeInitialization 和 postProcessAfterInitialization 两个 “魔法工具”,能在 bean 初始化的前后瞬间介入,巧妙地修改 bean 实例,或者为其注入自定义的逻辑,就像为一件即将完工的艺术品进行最后的雕琢,让 bean 更加贴合业务需求。

@Configuration
public class CustomBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("targetBean")) {
            // 在初始化前进行处理,比如给Bean的某个属性赋值(这里简单示例修改属性值)
            if (bean instanceof TargetBean) {
                ((TargetBean) bean).setMessage("Modified message before initialization");
            }
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("targetBean")) {
            // 在初始化后进行处理,例如添加一些额外逻辑,这里简单打印一句话
            System.out.println("Bean has been initialized, performing additional logic after initialization.");
        }
        return bean;
    }
}

class TargetBean {
    private String message = "Original message";

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

在上述代码里:

  • 定义了CustomBeanPostProcessor实现BeanPostProcessor接口,重写了postProcessBeforeInitializationpostProcessAfterInitialization两个方法。
  • postProcessBeforeInitialization方法中,当Bean的名称为"targetBean"时,对其属性进行修改(示例中修改了message属性值)。
  • postProcessAfterInitialization方法中,针对同样名为"targetBean"Bean,在其初始化后添加了打印额外逻辑的操作,以此展示在Bean初始化前后进行介入处理的功能。

此外,实现 InitializingBean 接口,无疑是为 bean 赋予了自我完善的能力,在属性填充完毕后,自动触发 afterPropertiesSet 方法,让 bean 有机会进行最后的自我调整,以最佳状态迎接后续挑战;而自定义构造方法,更是给予开发者掌控 bean 诞生之初模样的权力,通过不同参数组合,精准构建出符合特定场景需求的 bean 实例。

@Configuration
public class CustomInitializingBean implements InitializingBean {

    private String property;

    public String getProperty() {
        return property;
    }

    public void setProperty(String property) {
        this.property = property;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        // 在属性填充完毕后自动触发的方法,这里进行一些自我调整,比如拼接字符串
        property = "Initialized: " + property;
    }
}

在这个示例中:

  • CustomInitializingBean类实现了InitializingBean接口,重写了afterPropertiesSet方法。
  • 当 Spring 容器为该Bean进行属性填充完成后,就会自动调用afterPropertiesSet方法,在这个示例里对property属性进行了修改,添加了前缀来展示其可以进行自我调整的功能。

(二)自定义操作的作用与好处

这些自定义操作,绝非华而不实的点缀,而是拥有实实在在改变应用走向的强大魔力。

通过 BeanDefinitionRegistryPostProcessor 动态注册 bean,能够在运行时灵活应变,比如在某个特定条件触发后,才将某个关键 bean 纳入容器管理,完美适配复杂多变的业务场景;BeanFactoryPostProcessor 对 bean 定义的修改,如同精准的手术刀,能够修复配置瑕疵,优化依赖注入,确保 bean 初始化万无一失;BeanPostProcessor 的介入,则像是为 bean 披上一层量身定制的防护衣,无论是添加日志记录、实施安全检查,还是动态代理增强,都能轻松实现,让 bean 更加健壮、智能。

自定义构造方法,更是在面对多态场景时游刃有余,以不同参数组合构建不同形态的 bean,满足多样化的业务诉求;实现 InitializingBean 接口,让 bean 拥有自我修复、自我优化的能力,确保投入使用时毫无破绽。它们就像是开发者手中的魔法棒,将 Spring 框架的灵活性与扩展性发挥到极致,让应用开发在面对各种复杂业务逻辑和严苛性能要求时,都能从容应对,轻松化解难题。

五、总结

至此,我们已然深入探究了 Spring 创建 bean 的曲折历程,从加载信息的前期筹备,到实例化的破土动工,从属性填充的精心雕琢,到初始化的精细打磨,再到后置操作的完美收官,每一步都饱含着 Spring 的设计智慧。三级缓存更是如同一把精密的手术刀,精准剖析循环依赖的症结,巧妙化解难题,确保 bean 的创建有条不紊。而那些自定义实现操作,则像是开发者的秘密武器,在各个关键节点灵活应变,让 Spring 框架能够完美适配千变万化的业务场景。

对于初涉 Spring 领域的开发者而言,建议从基础的 bean 创建示例入手,亲手搭建环境,逐步调试,感受每一个步骤的神奇变化。随着理解的深入,勇敢踏入源码的世界,探寻底层逻辑,相信每一次的深入钻研,都会带来全新的感悟与成长。在实际项目中,善用自定义操作,根据业务特点优化 bean 的创建与管理,让应用更加高效、健壮。愿大家在 Spring 的学习之路上砥砺前行,不断探索,收获更多知识与成长。