Sping源码(九)—— Bean的初始化(非懒加载)— Bean的创建方式(Supplier)

2024-06-16 21:12

本文主要是介绍Sping源码(九)—— Bean的初始化(非懒加载)— Bean的创建方式(Supplier),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

序言

目前介绍几种Spring创建对象的方式,其中包括 FactoryBean、Cglib动态代理、自定义BeanPostProcessor(InstantiationAwareBeanPostProcessor)。
这篇文章会继续扩展Spring中Bean的创建方式——Supplier。

Supplier

先看下我们的Supplier类,在前面的ObjectFactory中有提到过,类上有@FunctionalInterface注解相当于函数式编程,而当我们调用get()方法时,才会调用我们自定义实现的方法。

@FunctionalInterface
public interface Supplier<T> {/*** Gets a result.** @return a result*/T get();
}

测试类

SupplierBeanFactoryPostProcessor
自定义类实现了BFPP,并在方法中设置InstanceSupplier变量。

public class SupplierBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {GenericBeanDefinition gbd = (GenericBeanDefinition)beanFactory.getBeanDefinition("user");gbd.setInstanceSupplier(CreateSupplier::createUser);gbd.setBeanClass(User.class);}
}
// 或者
public class SupplierBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {registry.registerBeanDefinition("user", new RootBeanDefinition(User.class,CreateSupplier::createUser));}
}

CreateSupplier
返回User对象。

public class CreateSupplier {public static User createUser(){return new User("zhangsan");}
}

User
简单的一个User类。

public class User {private String name;public User() {}public User(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +'}';}
}

supplier.xml
xml文件中定义了SupplierBeanFactoryPostProcessor和准备创建的User类。

<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 http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="supplierBeanFactoryPostProcessor" class="org.springframework.supplier.SupplierBeanFactoryPostProcessor"/><bean id="user" class="org.springframework.supplier.User"/><bean id="supplierBeanDefinitionRegistryPostProcessor" class="org.springframework.supplier.SupplierBeanDefinitionRegistryPostProcessor" /></beans>

main
当代码运行,refresh()主流程方法中调用invokeBeanFactoryPostProcessors()方法时,就会执行我们自定义的类中方法,从而设置instanceSupplier变量,当调用getBean方法生成User对象时,instanceSupplier不为null,调用createBean方法,生成User对象。

public static void main(String[] args) {ApplicationContext ac = new ClassPathXmlApplicationContext("supplier.xml");User user = ac.getBean(User.class);System.out.println(user.toString());}

doCreateBean

回到我们的源码doCreateBean方法中,我们重点关注createBeanInstance()方法。

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {// Instantiate the bean.//这个beanWrapper是用来持有创建出来的bean对象的BeanWrapper instanceWrapper = null;//如果是单例对象,从factoryBeanInstanceCache缓存中移除该信息if (mbd.isSingleton()) {// 如果是单例对象,从factoryBean实例缓存中移除当前bean定义信息instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);}// 没有就创建实例if (instanceWrapper == null) {// 根据执行bean使用对应的策略创建新的实例,如,工厂方法,构造函数主动注入、简单初始化instanceWrapper = createBeanInstance(beanName, mbd, args);}// 去除无用代码..... }

createBeanInstance
createBeanInstance方法中,会获取我们自定义的getInstanceSupplier,如果不为 null, 则调用obtainFromSupplier

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {// Make sure bean class is actually resolved at this point.// 锁定class,根据设置的class属性或者根据className来解析classClass<?> beanClass = resolveBeanClass(mbd, beanName);// 如果beanClass != null// 并且,访问修饰符不是public修饰, 抛异常if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());}// 获取定义的SupplierSupplier<?> instanceSupplier = mbd.getInstanceSupplier();if (instanceSupplier != null) {return obtainFromSupplier(instanceSupplier, beanName);}if (mbd.getFactoryMethodName() != null) {return instantiateUsingFactoryMethod(beanName, mbd, args);}return instantiateBean(beanName, mbd);}

此时创建我们的User对象时,可以进到判断中。
在这里插入图片描述
obtainFromSupplier
此时instanceSupplier.get()方法就会执行我们定义的createUser,从而进行User对象的创建。

protected BeanWrapper obtainFromSupplier(Supplier<?> instanceSupplier, String beanName) {Object instance;// 在缓存中获取原先创建的beanString outerBean = this.currentlyCreatedBean.get();// 用当前BeanName做替换this.currentlyCreatedBean.set(beanName);try {// 调用supplier.get()方法进行实例化instance = instanceSupplier.get();}finally {//if (outerBean != null) {this.currentlyCreatedBean.set(outerBean);}else {this.currentlyCreatedBean.remove();}}// 如果instance == null,则返回NullBeanif (instance == null) {instance = new NullBean();}// 将创建好的示例封装到wrapper包装类BeanWrapper bw = new BeanWrapperImpl(instance);initBeanWrapper(bw);return bw;}

Supplier对象是我们自定义的Supplier。
在这里插入图片描述

这篇关于Sping源码(九)—— Bean的初始化(非懒加载)— Bean的创建方式(Supplier)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1067557

相关文章

C++中RAII资源获取即初始化

《C++中RAII资源获取即初始化》RAII通过构造/析构自动管理资源生命周期,确保安全释放,本文就来介绍一下C++中的RAII技术及其应用,具有一定的参考价值,感兴趣的可以了解一下... 目录一、核心原理与机制二、标准库中的RAII实现三、自定义RAII类设计原则四、常见应用场景1. 内存管理2. 文件操

C++中零拷贝的多种实现方式

《C++中零拷贝的多种实现方式》本文主要介绍了C++中零拷贝的实现示例,旨在在减少数据在内存中的不必要复制,从而提高程序性能、降低内存使用并减少CPU消耗,零拷贝技术通过多种方式实现,下面就来了解一下... 目录一、C++中零拷贝技术的核心概念二、std::string_view 简介三、std::stri

Linux脚本(shell)的使用方式

《Linux脚本(shell)的使用方式》:本文主要介绍Linux脚本(shell)的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录概述语法详解数学运算表达式Shell变量变量分类环境变量Shell内部变量自定义变量:定义、赋值自定义变量:引用、修改、删

Python中使用uv创建环境及原理举例详解

《Python中使用uv创建环境及原理举例详解》uv是Astral团队开发的高性能Python工具,整合包管理、虚拟环境、Python版本控制等功能,:本文主要介绍Python中使用uv创建环境及... 目录一、uv工具简介核心特点:二、安装uv1. 通过pip安装2. 通过脚本安装验证安装:配置镜像源(可

python判断文件是否存在常用的几种方式

《python判断文件是否存在常用的几种方式》在Python中我们在读写文件之前,首先要做的事情就是判断文件是否存在,否则很容易发生错误的情况,:本文主要介绍python判断文件是否存在常用的几种... 目录1. 使用 os.path.exists()2. 使用 os.path.isfile()3. 使用

Mybatis的分页实现方式

《Mybatis的分页实现方式》MyBatis的分页实现方式主要有以下几种,每种方式适用于不同的场景,且在性能、灵活性和代码侵入性上有所差异,对Mybatis的分页实现方式感兴趣的朋友一起看看吧... 目录​1. 原生 SQL 分页(物理分页)​​2. RowBounds 分页(逻辑分页)​​3. Page

Java中实现线程的创建和启动的方法

《Java中实现线程的创建和启动的方法》在Java中,实现线程的创建和启动是两个不同但紧密相关的概念,理解为什么要启动线程(调用start()方法)而非直接调用run()方法,是掌握多线程编程的关键,... 目录1. 线程的生命周期2. start() vs run() 的本质区别3. 为什么必须通过 st

Linux链表操作方式

《Linux链表操作方式》:本文主要介绍Linux链表操作方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、链表基础概念与内核链表优势二、内核链表结构与宏解析三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势六、典型应用场景七、调试技巧与

Linux实现线程同步的多种方式汇总

《Linux实现线程同步的多种方式汇总》本文详细介绍了Linux下线程同步的多种方法,包括互斥锁、自旋锁、信号量以及它们的使用示例,通过这些同步机制,可以解决线程安全问题,防止资源竞争导致的错误,示例... 目录什么是线程同步?一、互斥锁(单人洗手间规则)适用场景:特点:二、条件变量(咖啡厅取餐系统)工作流

springboot加载不到nacos配置中心的配置问题处理

《springboot加载不到nacos配置中心的配置问题处理》:本文主要介绍springboot加载不到nacos配置中心的配置问题处理,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑... 目录springboot加载不到nacos配置中心的配置两种可能Spring Boot 版本Nacos