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++11范围for初始化列表auto decltype详解

《C++11范围for初始化列表autodecltype详解》C++11引入auto类型推导、decltype类型推断、统一列表初始化、范围for循环及智能指针,提升代码简洁性、类型安全与资源管理效... 目录C++11新特性1. 自动类型推导auto1.1 基本语法2. decltype3. 列表初始化3

最新Spring Security的基于内存用户认证方式

《最新SpringSecurity的基于内存用户认证方式》本文讲解SpringSecurity内存认证配置,适用于开发、测试等场景,通过代码创建用户及权限管理,支持密码加密,虽简单但不持久化,生产环... 目录1. 前言2. 因何选择内存认证?3. 基础配置实战❶ 创建Spring Security配置文件

Python获取浏览器Cookies的四种方式小结

《Python获取浏览器Cookies的四种方式小结》在进行Web应用程序测试和开发时,获取浏览器Cookies是一项重要任务,本文我们介绍四种用Python获取浏览器Cookies的方式,具有一定的... 目录什么是 Cookie?1.使用Selenium库获取浏览器Cookies2.使用浏览器开发者工具

Java获取当前时间String类型和Date类型方式

《Java获取当前时间String类型和Date类型方式》:本文主要介绍Java获取当前时间String类型和Date类型方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录Java获取当前时间String和Date类型String类型和Date类型输出结果总结Java获取

Spring Bean初始化及@PostConstruc执行顺序示例详解

《SpringBean初始化及@PostConstruc执行顺序示例详解》本文给大家介绍SpringBean初始化及@PostConstruc执行顺序,本文通过实例代码给大家介绍的非常详细,对大家的... 目录1. Bean初始化执行顺序2. 成员变量初始化顺序2.1 普通Java类(非Spring环境)(

C#监听txt文档获取新数据方式

《C#监听txt文档获取新数据方式》文章介绍通过监听txt文件获取最新数据,并实现开机自启动、禁用窗口关闭按钮、阻止Ctrl+C中断及防止程序退出等功能,代码整合于主函数中,供参考学习... 目录前言一、监听txt文档增加数据二、其他功能1. 设置开机自启动2. 禁止控制台窗口关闭按钮3. 阻止Ctrl +

linux批量替换文件内容的实现方式

《linux批量替换文件内容的实现方式》本文总结了Linux中批量替换文件内容的几种方法,包括使用sed替换文件夹内所有文件、单个文件内容及逐行字符串,强调使用反引号和绝对路径,并分享个人经验供参考... 目录一、linux批量替换文件内容 二、替换文件内所有匹配的字符串 三、替换每一行中全部str1为st

Olingo分析和实践之OData框架核心组件初始化(关键步骤)

《Olingo分析和实践之OData框架核心组件初始化(关键步骤)》ODataSpringBootService通过初始化OData实例和服务元数据,构建框架核心能力与数据模型结构,实现序列化、URI... 目录概述第一步:OData实例创建1.1 OData.newInstance() 详细分析1.1.1

从入门到精通详解LangChain加载HTML内容的全攻略

《从入门到精通详解LangChain加载HTML内容的全攻略》这篇文章主要为大家详细介绍了如何用LangChain优雅地处理HTML内容,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录引言:当大语言模型遇见html一、HTML加载器为什么需要专门的HTML加载器核心加载器对比表二

Python实现终端清屏的几种方式详解

《Python实现终端清屏的几种方式详解》在使用Python进行终端交互式编程时,我们经常需要清空当前终端屏幕的内容,本文为大家整理了几种常见的实现方法,有需要的小伙伴可以参考下... 目录方法一:使用 `os` 模块调用系统命令方法二:使用 `subprocess` 模块执行命令方法三:打印多个换行符模拟