【手撕 Spring】 -- 实现含构造函数的类实例化

news/2024/11/17 17:57:13/

🌈手写简化版 Spring 框架通过构建一个精简版的 Spring 框架,深入理解 Spring 的核心机制,掌握其设计思想,进一步提升编程能力

🌈项目代码地址:https://github.com/YYYUUU42/mini-Spring

如果该项目对你有帮助,可以在 github 上点个 ⭐ 喔 🥰🥰

🌈手撕 Spring 系列可以点开专栏,参看完整的文档

目录

1. 设计目的

2. 新增内容

2.1. InstantiationStrategy

2.2. SimpleInstantiationStrategy

2.3. CglibSubclassingInstantiationStrategy

2.4. AbstractBeanFactory

2.5. AbstractAutowireCapableBeanFactory

3. 调用流程

4. 类图


1. 设计目的

在上一章中扩展了 Bean 容器功能,实现了类的实例化由自定义的 Spring 容器完成。然而,目前尚未支持带有参数构造器的类的实例化。接下来,将对 BeanFactory 进行改造,使其能够获取含参构造的 Bean。实现带参构造函数的类的实例化功能。

2. 新增内容

2.1. InstantiationStrategy

定义了实例化策略的接口,入参包括 BeanDefinitionbeanName、类的构造函数(Constructor)、以及构造函数的参数(args)。该接口的实现类负责具体的对象实例化逻辑。

java">/*** @description Bean 实例化策略*/
public interface InstantiationStrategy {/*** 实例化*/Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException;}

2.2. SimpleInstantiationStrategy

通过 JDK 自带的 Class 类的 getDeclaredConstructor 方法获取指定的构造函数,然后通过反射的 newInstance 方法实例化对象。这是基于 JDK 原生反射机制的实例化方式。

java">/*** @description 使用 JDK 反射机制 来实例化对象*/
public class SimpleInstantiationStrategy implements InstantiationStrategy {@Overridepublic Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException {Class<?> clazz = beanDefinition.getBeanClass();try {if (ctor != null) {return clazz.getDeclaredConstructor(ctor.getParameterTypes()).newInstance(args);} else {return clazz.getDeclaredConstructor().newInstance();}} catch (NoSuchMethodException | InstantiationException | IllegalAccessException |InvocationTargetException e) {throw new BeansException("Failed to instantiate [" + clazz.getName() + "]", e);}}}

2.3. CglibSubclassingInstantiationStrategy

使用 Cglib 的 Enhancer 类进行动态代理,生成目标类的子类并实例化对象。Cglib 提供了一种无需依赖接口的代理机制,适用于需要动态代理类的场景。

java">/*** @description Cglib 实例化*/
public class CglibSubclassingInstantiationStrategy implements InstantiationStrategy {/*** 通过 Cglib 动态生成子类,并根据传入的构造函数和参数来实例化对象** @param ctor 构造函数对象,用于指定通过哪个构造函数来实例化对象* @param args 构造函数参数,传递给构造函数用于实例化对象*/@Overridepublic Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException {// 动态生成子类Enhancer enhancer = new Enhancer();enhancer.setSuperclass(beanDefinition.getBeanClass());enhancer.setCallback(new NoOp() {@Overridepublic int hashCode() {return super.hashCode();}});if (ctor == null) {return enhancer.create();}return enhancer.create(ctor.getParameterTypes(), args);}}

2.4. AbstractBeanFactory

利用 Java 的方法重载机制,提供了 getBean 方法的多个重载版本,分别支持有构造函数参数和无构造函数参数的 Bean 获取方式。这为灵活的 Bean 实例化提供了基础。

java">/*** 核心功能:* 1. Bean 获取:首先尝试从单例缓存中获取 Bean。如果没有找到,则通过 Bean 定义创建新的 Bean 实例。* 2. Bean 定义的获取:提供一个抽象方法 `getBeanDefinition`,子类负责实现该方法,从而获取对应 Bean 的定义信息。* 3. 创建 Bean:提供一个抽象方法 `createBean`,子类负责实现该方法,用于根据 Bean 定义实例化对象。*/
public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory {@Overridepublic Object getBean(String name) throws BeansException {return doGetBean(name, null);}@Overridepublic Object getBean(String name, Object... args) throws BeansException {return doGetBean(name, args);}@SuppressWarnings("unchecked")protected <T> T doGetBean(final String name, final Object[] args) {Object bean = getSingleton(name);if (bean != null) {return (T) bean;}BeanDefinition beanDefinition = getBeanDefinition(name);return (T) createBean(name, beanDefinition, args);}protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException;protected abstract Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException;
}

2.5. AbstractAutowireCapableBeanFactory

新增 createBeanInstance 方法,通过 JDK 自带的 Class.getDeclaredConstructors() 获取目标类的所有构造函数。根据传入的参数,选择合适的构造函数,然后调用 SimpleInstantiationStrategyCglibSubclassingInstantiationStrategy 实现类的实例化方法,以生成 Bean 实例。

java">/*** @description 负责通过反射机制创建 Bean 实例,并提供了将其注册为单例的能力*/
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();/*** 创建 Bean 实例并将其注册为单例*/@Overrideprotected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {Object bean = null;try {bean = createBeanInstance(beanDefinition, beanName, args);} catch (Exception e) {throw new BeansException("Instantiation of bean failed", e);}addSingleton(beanName, bean);return bean;}/*** 创建 bean 实例*/protected Object createBeanInstance(BeanDefinition beanDefinition, String beanName, Object[] args) {Constructor<?> constructorToUse = null;Class<?> beanClass = beanDefinition.getBeanClass();Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors();// 遍历所有声明的构造函数,找到参数数量与传入的 args 参数数量匹配的构造函数for (Constructor<?> ctor : declaredConstructors) {if (args != null && ctor.getParameterTypes().length == args.length) {constructorToUse = ctor;break;}}return getInstantiationStrategy().instantiate(beanDefinition, beanName, constructorToUse, args);}public InstantiationStrategy getInstantiationStrategy() {return instantiationStrategy;}public void setInstantiationStrategy(InstantiationStrategy instantiationStrategy) {this.instantiationStrategy = instantiationStrategy;}
}

3. 调用流程

  1. 实例化 DefaultListableBeanFactory:首先实例化 DefaultListableBeanFactory,它内部维护了一个 beanDefinitionMap 容器,用于存储 BeanDefinition 对象。
  2. 注册 BeanDefinition:实例化 BeanDefinition,并将 UserService.class 的元数据信息存入其中。随后将该 BeanDefinition 注册到 beanDefinitionMap 容器中。
  3. 获取 UserService 的 Bean:调用 getBean 方法时,首先尝试从 DefaultSingletonBeanRegistry 的单例缓存容器 singletonObjects 中获取已创建的 UserService 实例。如果获取不到,则进入 Bean 创建流程。
  4. 获取 BeanDefinition:从 beanDefinitionMap 中获取 UserService 的 BeanDefinition,从中提取 UserService 类的元数据信息。
  5. 获取构造函数:通过 UserService 类的元数据信息获取其所有构造函数,并根据传入的参数(如果存在)选择合适的构造函数。由于类可能存在多个重载构造函数,因此需要根据参数类型匹配合适的构造函数。
  6. 实例化 UserService:根据选定的构造函数,调用 SimpleInstantiationStrategy 或 CglibSubclassingInstantiationStrategy 实例化策略,生成 UserService 对象并返回给调用方。

4. 类图


http://www.ppmy.cn/news/1547772.html

相关文章

计算机网络 (5)数据通信的基础知识

前言 数据通信是一种以信息处理技术和计算机技术为基础的通信方式&#xff0c;它通过数据通信系统将数据以某种信号方式从一处传送到另一处&#xff0c;为计算机网络的应用和发展提供了技术支持和可靠的通信环境&#xff0c;是现代通信技术的关键部分。 一、数据通信的基本概念…

C# DataTable使用Linq查询详解

前奏- C# 对DataTable进行查询 C# 可以对 DataTable 进行查询。在 .NET 框架中&#xff0c;DataTable 类提供了几种方法来查询数据&#xff0c;包括 Select 方法和 AsEnumerable 扩展方法&#xff08;在 System.Data.DataSetExtensions 命名空间中&#xff09;。 使用 Select…

机器学习: LightGBM模型(优化版)——高效且强大的树形模型

LightGBM&#xff08;Light Gradient Boosting Machine&#xff09;是一种基于梯度提升决策树&#xff08;GBDT&#xff09;的框架&#xff0c;由微软提出。它具有高效的训练速度、低内存占用、支持并行和GPU加速等特点&#xff0c;非常适合大规模数据的训练任务&#xff0c;尤…

1、使用vscode+eide+stm32cubeMx开发stm32

步骤1&#xff1a;在vscode中安装如下的插件 步骤2&#xff1a;点击Embedded IDE&#xff0c;点击“新建项目”-----空项目-----Cortex-M项目。 步骤3&#xff1a;输入项目名&#xff0c;回车后会要制定保存路径&#xff0c;此时就是一个已项目名命名的文件夹。 步骤4&#xff…

鸿蒙next ui安全区域适配(刘海屏、摄像头挖空等)

目录 相关api 团结引擎对于鸿蒙的适配已经做了安全区域的适配&#xff0c;也考虑到了刘海屏和摄像机挖孔的情况&#xff0c;在团结引擎内可以直接使用Screen.safeArea 相关api 团结引擎对于鸿蒙的适配已经做了安全区域的适配&#xff0c;也考虑到了刘海屏和摄像机挖孔的情况&am…

传奇996_19——龙岭总结

功能&#xff1a; 切割 切割属性&#xff1a; 即人物属性&#xff0c;可以设置临时属性或者永久属性&#xff0c;龙岭使用的是临时属性&#xff0c;所谓临时就是存在有效期&#xff0c;龙岭设置的有效期是123456789秒&#xff0c;即1428.89802天。 龙岭写法&#xff08;倒叙…

提取 Docker 镜像的 Dockerfile 工具集

在 Docker 开发和运维过程中,我们经常需要分析或重建已有镜像的 Dockerfile。无论是为了理解镜像的构建过程、优化镜像大小,还是出于安全审计的需求,能够从现有镜像中提取或重建 Dockerfile 都是一项非常有用的技能。本文将介绍一系列可以帮助我们完成这项任务的工具。 © …

Github客户端工具github-desktop使用教程

文章目录 1.客户端工具的介绍2.客户端工具使用感受3.仓库的创建4.初步尝试5.本地文件和仓库路径5.1原理说明5.2修改文件5.3版本号的说明5.4结合码云解释5.5版本号的查找 6.分支管理6.1分支的引入6.2分支合并6.3创建测试仓库6.4创建测试分支6.5合并分支6.6合并效果查看6.7分支冲…