当前位置: 首页 > article >正文

简识Spring创建Bean方式和设计模式

一、理论解释:

Spring在创建Bean时主要有四种方式,这些方式分别涉及到了不同的设计模式。以下是具体的创建方式及对应的设计模式:

  1. 通过反射调用构造方法创建Bean

    • 方式:在Spring的配置文件中,使用<bean>标签的class属性指定要创建的Bean的完整类名。Spring容器内部会自动调用该类型的无参构造方法(或带参数的构造方法,此时需要配合<constructor-arg>标签指定参数)来创建Bean对象,并将其放在容器中以供使用。
    • 设计模式:这种方式主要使用了简单工厂模式的思想,即通过一个工厂类(在这里是Spring容器)来创建对象,但不需要显式地编写工厂类代码。不过,严格来说,由于Spring容器本身是一个复杂的框架,其内部实现可能涉及到了更多的设计模式,但在此处我们主要关注其对外提供的创建Bean的方式。
  2. 通过静态工厂方法创建Bean

    • 方式:首先编写一个包含静态方法的工厂类,该方法返回要创建的Bean对象。然后在Spring的配置文件中,使用<bean>标签的class属性指定工厂类的完整类名,并使用factory-method属性指定静态工厂方法名。Spring容器会自动调用该静态方法来获取Bean对象。
    • 设计模式:这种方式明确使用了静态工厂模式。静态工厂模式是一种创建型设计模式,它通过一个包含静态方法的类来创建对象,而不需要将对象作为类的成员。
  3. 通过实例工厂方法创建Bean

    • 方式:首先编写一个普通的工厂类(非静态),该类包含一个返回要创建的Bean对象的方法。然后在Spring的配置文件中,先使用<bean>标签创建一个工厂类的实例(即工厂Bean),再使用另一个<bean>标签的factory-bean属性指定工厂Bean的ID,并使用factory-method属性指定工厂方法名。Spring容器会先创建工厂Bean实例,然后调用其方法来获取Bean对象。
    • 设计模式:这种方式使用了工厂方法模式。工厂方法模式是一种创建型设计模式,它通过一个工厂类的方法来创建对象,但工厂类本身是一个普通类(非静态类)。
  4. 通过FactoryBean创建Bean

    • 方式:编写一个实现FactoryBean接口的类,该接口包含三个方法:getObject()用于返回要创建的Bean对象,getObjectType()用于返回Bean对象的类型,isSingleton()用于指定Bean对象是否为单例。然后在Spring的配置文件中,使用<bean>标签的class属性指定FactoryBean实现类的完整类名。Spring容器会自动调用FactoryBeangetObject()方法来获取Bean对象。
    • 设计模式:这种方式虽然表面上看起来与普通的Bean创建方式类似,但实际上它利用了FactoryBean接口的特殊性质来实现了一种更灵活的Bean创建方式。这种方式可以看作是对工厂方法模式的一种扩展或变种,因为它允许在创建Bean对象时进行更多的自定义操作(如延迟加载、代理创建等)。不过,从更广泛的角度来看,它也可以被视为一种代理模式的应用,因为FactoryBean实际上是在为真正的Bean对象创建一个代理或包装器。

综上所述,Spring在创建Bean时主要使用了简单工厂模式(通过反射调用构造方法)、静态工厂模式、工厂方法模式和代理模式(通过FactoryBean)等设计模式。这些模式的应用使得Spring能够灵活地创建和管理Bean对象,从而满足了复杂应用程序的需求。

------分界线--------------------------------------------------------------------------------------------------------------

二、示例代码解释

当然,下面我将根据上面提到的几种创建Bean的方式和对应的设计模式,给出具体的示例代码,并简要说明每个示例以及需要注意的事项。

1. 通过反射调用构造方法创建Bean

示例代码

// 定义一个简单的Bean类
public class MyBean {private String name;// 无参构造方法public MyBean() {}// 带参数的构造方法(虽然在这个例子中未使用,但Spring支持)public MyBean(String name) {this.name = name;}// Getter和Setter方法public String getName() {return name;}public void setName(String name) {this.name = name;}
}// Spring配置文件(applicationContext.xml)
<bean id="myBean" class="com.example.MyBean"/>

说明

  • 在Spring配置文件中,通过<bean>标签的class属性指定了要创建的Bean类。
  • Spring容器会调用该类的无参构造方法来创建Bean实例。

注意事项

  • 确保Bean类有一个可访问的无参构造方法。
  • 如果需要注入依赖,可以使用<property><constructor-arg>标签。

2. 通过静态工厂方法创建Bean

示例代码

// 静态工厂类
public class MyBeanFactory {// 静态工厂方法public static MyBean createMyBean() {return new MyBean();}
}// Spring配置文件(applicationContext.xml)
<bean id="myBean" class="com.example.MyBeanFactory" factory-method="createMyBean"/>

说明

  • 在Spring配置文件中,通过<bean>标签的class属性指定了工厂类,并通过factory-method属性指定了静态工厂方法。
  • Spring容器会调用该静态方法来创建Bean实例。

注意事项

  • 确保工厂方法是静态的。
  • 工厂方法返回的对象类型应与配置文件中声明的Bean类型一致。

3. 通过实例工厂方法创建Bean

示例代码

// 实例工厂类
public class MyInstanceFactory {// 实例工厂方法public MyBean createMyBean() {return new MyBean();}
}// Spring配置文件(applicationContext.xml)
<!-- 首先创建工厂Bean实例 -->
<bean id="myInstanceFactory" class="com.example.MyInstanceFactory"/>
<!-- 然后通过工厂Bean实例创建目标Bean -->
<bean id="myBean" factory-bean="myInstanceFactory" factory-method="createMyBean"/>

说明

  • 在Spring配置文件中,首先通过<bean>标签创建了一个工厂Bean实例。
  • 然后通过另一个<bean>标签的factory-bean属性指定了工厂Bean的ID,并通过factory-method属性指定了工厂方法。
  • Spring容器会先创建工厂Bean实例,然后调用其方法来创建目标Bean实例。

注意事项

  • 确保工厂方法是实例方法(非静态)。
  • 工厂Bean实例和目标Bean实例的生命周期是独立的。

4. 通过FactoryBean创建Bean

示例代码

// 实现FactoryBean接口的类
public class MyFactoryBean implements FactoryBean<MyBean> {@Overridepublic MyBean getObject() throws Exception {// 这里可以执行一些自定义逻辑,如创建代理、延迟加载等return new MyBean();}@Overridepublic Class<?> getObjectType() {return MyBean.class;}@Overridepublic boolean isSingleton() {// 指定Bean是否为单例return true;}
}// Spring配置文件(applicationContext.xml)
<bean id="myFactoryBean" class="com.example.MyFactoryBean"/>
// 注意:这里实际上获取的是FactoryBean的getObject()方法返回的对象,而不是FactoryBean本身
// 因此,在获取Bean时,不需要指定FactoryBean的ID,而是直接获取目标Bean的ID(尽管这里我们没有直接定义目标Bean的ID)
// 但由于我们使用了FactoryBean,我们通常不会这样做。在实际应用中,我们可能会通过其他方式(如依赖注入)来使用这个Bean。
// 如果确实需要通过ID获取,可以这样做(但这不是FactoryBean的典型用法):
// <bean id="myBean" factory-bean="myFactoryBean" factory-method="getObject"/>
// 但通常,我们只需要配置FactoryBean,然后在需要MyBean的地方让Spring自动注入即可。

注意:上面的配置文件中关于myBean的声明是不典型的,因为当你配置了一个FactoryBean后,你通常不会再去显式地通过factory-beanfactory-method去获取它产生的对象。相反,Spring会在你需要注入MyBean类型的地方自动使用MyFactoryBean来产生对象。因此,在实际应用中,你可能不会看到像上面那样的myBean声明。

典型用法

在需要使用MyBean的地方,你只需声明一个依赖,Spring会自动使用MyFactoryBean来提供这个依赖。例如:

@Component
public class SomeService {// Spring会自动注入MyFactoryBean产生的MyBean实例private final MyBean myBean;@Autowiredpublic SomeService(MyBean myBean) {this.myBean = myBean;}// ... 使用myBean的方法 ...
}

在这个例子中,SomeService类依赖于MyBean类型,而Spring会自动使用MyFactoryBean来创建并注入这个依赖。

说明

  • FactoryBean接口允许你自定义Bean的创建逻辑。
  • getObject()方法返回实际要注入的Bean实例。
  • getObjectType()方法返回Bean实例的类型。
  • isSingleton()方法指定Bean是否为单例。

注意事项

  • FactoryBean通常用于创建复杂的Bean实例,如代理对象、延迟加载对象等。
  • 确保getObject()方法返回的对象类型与FactoryBean接口中声明的泛型类型一致。
  • 当使用FactoryBean时,通常不需要在配置文件中显式地声明目标Bean的ID,因为Spring会自动处理这部分逻辑。

------分界线--------------------------------------------------------------------------------------------------------------

三、饿汉模式、饱汉模式

在您之前提到的Spring Bean创建方式中,并没有直接包含饿汉模式(Eager Initialization)和饱汉模式(Lazy Initialization)这两种单例模式的实现。饿汉模式和饱汉模式是单例模式在Java中的两种常见实现方式,它们主要用于确保一个类只有一个实例,并提供一个全局访问点来获取该实例。

1、饿汉模式(Eager Initialization)

饿汉模式在类加载时就完成了实例的创建。因此,它是线程安全的,因为JVM在加载类时只会创建一个类实例。但是,如果实例从未被使用过,那么这种提前创建实例的方式可能会浪费资源。

示例代码

public class EagerSingleton {// 在类加载时就创建实例private static final EagerSingleton INSTANCE = new EagerSingleton();// 私有构造方法防止外部实例化private EagerSingleton() {}// 提供全局访问点public static EagerSingleton getInstance() {return INSTANCE;}
}


2、饱汉模式(Lazy Initialization)

饱汉模式在第一次调用getInstance()方法时才创建实例。这种方式节省了资源,但如果多个线程同时调用该方法,则可能会导致多个实例被创建(即不是线程安全的)。为了解决这个问题,通常需要使用同步机制。

示例代码(非线程安全版本)

public class LazySingleton {// 声明实例变量但不立即初始化private static LazySingleton instance;// 私有构造方法防止外部实例化private LazySingleton() {}// 提供全局访问点(非线程安全)public static LazySingleton getInstance() {if (instance == null) {instance = new LazySingleton();}return instance;}
}


示例代码(线程安全版本,使用同步方法)

public class ThreadSafeLazySingleton {// 声明实例变量但不立即初始化private static ThreadSafeLazySingleton instance;// 私有构造方法防止外部实例化private ThreadSafeLazySingleton() {}// 提供全局访问点(线程安全,使用同步方法)public static synchronized ThreadSafeLazySingleton getInstance() {if (instance == null) {instance = new ThreadSafeLazySingleton();}return instance;}
}

或者,使用双重检查锁定(Double-Checked Locking)来优化性能:

public class DoubleCheckedLockingSingleton {// 使用volatile关键字确保instance变量的可见性private static volatile DoubleCheckedLockingSingleton instance;// 私有构造方法防止外部实例化private DoubleCheckedLockingSingleton() {}// 提供全局访问点(线程安全,使用双重检查锁定)public static DoubleCheckedLockingSingleton getInstance() {if (instance == null) {synchronized (DoubleCheckedLockingSingleton.class) {if (instance == null) {instance = new DoubleCheckedLockingSingleton();}}}return instance;}
}


3、异同点

  • 相同点
    • 两者都是单例模式的实现方式。
    • 都提供了全局访问点来获取类的唯一实例。
  • 不同点
    • 实例创建时机:饿汉模式在类加载时就创建实例,而饱汉模式在第一次调用getInstance()方法时才创建实例。
    • 资源消耗:饿汉模式可能会浪费资源(如果实例从未被使用过),而饱汉模式节省了资源。
    • 线程安全性:饿汉模式是线程安全的(因为实例在类加载时就创建了),而未经同步处理的饱汉模式不是线程安全的(可能会导致多个实例被创建)。但是,通过添加同步机制或使用双重检查锁定,可以使饱汉模式变得线程安全。

在Spring框架中,单例Bean的创建通常是由Spring容器管理的,而不是通过饿汉模式或饱汉模式来实现的。Spring容器负责确保每个单例Bean在容器中只有一个实例,并提供依赖注入机制来访问这些Bean。然而,Spring也支持懒加载(Lazy Initialization),这类似于饱汉模式中的延迟创建实例的概念,但它是通过Spring的配置来实现的,而不是通过单例模式本身的实现方式。

(抱歉,最近在面试,粗糙了些。)

(望各位潘安、各位子健/各位彦祖、于晏不吝赐教!多多指正!🙏)

相关文章:

简识Spring创建Bean方式和设计模式

一、理论解释&#xff1a; Spring在创建Bean时主要有四种方式&#xff0c;这些方式分别涉及到了不同的设计模式。以下是具体的创建方式及对应的设计模式&#xff1a; 通过反射调用构造方法创建Bean&#xff1a; 方式&#xff1a;在Spring的配置文件中&#xff0c;使用<bean…...

归并排序 Listnode* vector<int> vector<ListNode*>

加粗样式 ListNode* merge(ListNode* l1,ListNode* l2){ListNode* dummyheadnew ListNode(0);ListNode* curdummyhead;while(l1&&l2){if(l1->val>l2->val){cur->nextl2;l2l2->next;curcur->next;}else if(l1->val<l2->val){cur->nextl1…...

深度解析:大模型在多显卡服务器下的通信机制与分布式训练——以DeepSeek、Ollama和vLLM为例

一、引言&#xff1a;大模型与多显卡的必然结合 随着大模型参数规模突破千亿级&#xff08;如GPT-4、DeepSeek&#xff09;&#xff0c;单显卡的显存容量与算力已无法满足需求。多显卡并行计算成为训练与推理的核心技术&#xff0c;其核心挑战在于高效通信与负载均衡。本文以国…...

鸿蒙NEXT应用App测试-专项测试(DevEco Testing)

注意&#xff1a;大家记得先学通用测试在学专项测试 鸿蒙NEXT应用App测试-通用测试-CSDN博客 注意&#xff1a;博主有个鸿蒙专栏&#xff0c;里面从上到下有关于鸿蒙next的教学文档&#xff0c;大家感兴趣可以学习下 如果大家觉得博主文章写的好的话&#xff0c;可以点下关注…...

达梦数据库学习笔记@1

目录 达梦数据库学习笔记一、表空间管理&#xff08;一&#xff09;默认表空间&#xff08;二&#xff09;相关数据字典&#xff08;三&#xff09;表空间操作&#xff08;四&#xff09;临时表空间管理 二、重做日志管理&#xff08;一&#xff09;系统视图&#xff08;二&…...

设计模式| 观察者模式 Observer Pattern详解

目录 一、概述1.1 动机1.2 核心思想1.3 别名 二、角色与实现原理2.1 角色2.2 实现原理2.3 类图 三、经典接口实现3.1 示例3.1.1 观察者接口3.1.2 目标接口3.1.3 具体被观察者3.1.4 具体观察者3.1.5 Client3.1.6 UML时序图 3.2 特点 四、其他实现方式4.1 委托与事件&#xff08;…...

时间转换(acwing)c/c++/java/python

读取一个整数值&#xff0c;它是工厂中某个事件的持续时间&#xff08;以秒为单位&#xff09;&#xff0c;请你将其转换为小时&#xff1a;分钟&#xff1a;秒来表示。 输入格式 输入一个整数 NN。 输出格式 输出转换后的时间表示&#xff0c;格式为 hours:minutes:second…...

Rocky8 源码安装 HAProxy

HAProxy 是一款开源的高性能 负载均衡器 和 反向代理 软件&#xff0c;专注于处理高并发流量分发&#xff0c;广泛应用于企业级架构中提升服务的可用性、扩展性和安全性。 一、HAProxy 简介 1.1.HAProxy 是什么&#xff1f; 本质&#xff1a; 基于 C 语言开发 的轻量级工具&a…...

通过AI辅助生成PPT (by quqi99)

作者&#xff1a;张华 发表于&#xff1a;2025-02-23 版权声明&#xff1a;可以任意转载&#xff0c;转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明(http://blog.csdn.net/quqi99) 问题 媳妇需要将一个pdf文件中的某些部分做成PPT课件&#xff0c;我在想是…...

【从0做项目】Java文档搜索引擎(9)烧脑终章!

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 文章导读 零&#xff1a;项目结果展示 一&#xff1a;导入 二&#xff1a;问题引入 1&#xff1a;情…...

什么是 Cloud Studio DeepSeek ; 怎么实现Open WebUI快速体验

什么是 Cloud Studio DeepSeek ;怎么实现Open WebUI快速体验 一、概述 欢迎使用 Cloud Studio DeepSeek 工作空间!我们已为您预装并启动了以下服务,等待加载十几秒即可查看效果: Ollama 服务:支持通过 API 调用 DeepSeek 模型。 AnythingLLM 前端服务:提供交互式聊天界…...

rtconfig.cpython-313.pyc 在 .gitignore文件中写入 *.pyc 文件仍然没有被忽略?

在 .gitignore 文件中添加 *.pyc 和 *.*.pyc 规则时&#xff0c;如果 .pyc 文件仍然没有被忽略&#xff0c;可能有以下几种原因&#xff1a; 1. 已经被 Git 跟踪的文件 即使您在 .gitignore 中指定了忽略 .pyc 文件&#xff0c;Git 仍然会跟踪已经被提交到版本库中的文件。如…...

Linux 第二次脚本作业

1、需求&#xff1a;判断192.168.1.0/24网络中&#xff0c;当前在线的ip有哪些&#xff0c;并编写脚本打印出来。 2、设计一个 Shell 程序&#xff0c;在/userdata 目录下建立50个目录&#xff0c;即 user1~user50&#xff0c;并设置每个目录的权限&#xff0c;其中其他用户的权…...

mysql的源码包安装

安装方式一&#xff1a;&#xff08;编译好的直接安装&#xff09; 1.添加一块10G的硬盘&#xff0c;给root逻辑卷扩容 &#xff08;下面安装方式二有&#xff0c;一模一样的装就行&#xff0c;我就不写了&#xff0c;再写的话篇幅就太长了&#xff09; 2.下载编译好的源码包…...

《论面向对象的建模及应用》审题技巧 - 系统架构设计师

论面向对象的建模及应用写作框架 一、考点概述 本论题“论面向对象的建模及应用”主要考察软件测试工程师对面向对象建模技术的理解和应用能力。具体涵盖以下几个方面&#xff1a; 面向对象建模的基本概念 &#xff1a;这包括理解面向对象编程&#xff08;OOP&#xff09;的基…...

#渗透测试#批量漏洞挖掘#畅捷通T+远程命令执行漏洞

免责声明 本教程仅为合法的教学目的而准备,严禁用于任何形式的违法犯罪活动及其他商业行为,在使用本教程前,您应确保该行为符合当地的法律法规,继续阅读即表示您需自行承担所有操作的后果,如有异议,请立即停止本文章读。 目录 一、漏洞概况 二、攻击特征 三、应急处置…...

Sky Hackathon 清水湾的水 AI美食助手

这里写自定义目录标题 视频 视频 video...

【2024 CSDN博客之星】大学四年,我如何在CSDN实现学业与事业的“双逆袭”?

前言&#xff1a; Hello大家好&#xff0c;我是Dream。不知不觉2024年已经过去&#xff0c;自己也马上迈入23岁&#xff0c;感慨时间飞快&#xff0c;从19岁刚入大学加入CSDN&#xff0c;到现在大学毕业已经整整四年了。CSDN陪伴我走过了最青涩的四年大学时光&#xff0c;在这里…...

《AI赋能星际探索:机器人如何开启宇宙新征程!》

在人类对宇宙无尽的探索中&#xff0c;空间探索任务始终充满挑战。从遥远星球的探测&#xff0c;到空间站的维护&#xff0c;每一项任务都需要高精度、高可靠性的操作。人工智能&#xff08;AI&#xff09;的迅猛发展&#xff0c;为空间探索机器人带来了革命性的变革&#xff0…...

06排序 + 查找(D1_排序(D1_基础学习))

目录 学习预热&#xff1a;基础知识 一、什么是排序 二、为什么要排序 三、排序的稳定性 四、排序稳定性的意义 五、排序分类方式 方式一&#xff1a;内外分类 方式二&#xff1a;比较分类 六、排序算法性能评估 1. 算法的时间复杂度 2. 算法的空间复杂度 七、知识小…...

【数据挖掘】深度挖掘

【数据挖掘】深度挖掘 目录&#xff1a;1. 减少样本集的数量知识点示例 2. 对噪声比集剪枝知识点示例建立局部树代码示例&#xff08;使用 Python 和 scikit - learn 库构建局部决策树&#xff09;代码解释注意事项 最大超平面定义原理求解方法代码示例&#xff08;使用 Python…...

【Linux】基于UDP/TCP套接字编程与守护进程

目录 一、网路套接字编程 &#xff08;一&#xff09;基础概念 1、源IP地址与目的IP地址 2、端口号 3、TCP与UDP 4、网络字节序 &#xff08;二&#xff09;套接字编程接口 1、socket 常见API 2、sockaddr结构 &#xff08;三&#xff09;UDP套接字 1、UDP服务器创建…...

C++跳表实现,封装成Skiplist类

跳表 (Skip List) 是由 William Pugh 发明的一种查找数据结构&#xff0c;支持对数据的快速查找&#xff0c;插入和删除。 跳表的期望空间复杂度为O(n) &#xff0c;跳表的查询&#xff0c;插入和删除操作的期望时间复杂度都为O(logn)。 算法讲解149【扩展】有序表专题2-跳表_哔…...

探索与Cursor协作创建一个完整的前后端分离的项目的最佳实践

探索与Cursor协作创建一个完整的前后端分离的项目的最佳实践 Cursor简介 Cursor在目前代表了AI编程技术的顶峰。在一定程度上可以说是当今AI时代的最强生产力代表。为此,不惜重金开了年费会员来紧跟时代步伐。当然cline、roo code、trae等开源或者免费产品也在紧追不舍。 C…...

【uni-app】对齐胶囊容器组件

代码碎片 <template><div><view :style"{ height: ${statusBarHeight}px }"></view><viewclass"":style"{height: ${menuButtonHeight menuButtonPadding * 2}px,width: ${menuButtonInfo.left}px,}"><slot …...

podman加速器配置,harbor镜像仓库部署

Docker加速器 registries加速器 [rootlocalhost ~]# cat /etc/redhat-release CentOS Stream release 8 [rootlocalhost ~]# cd /etc/containers/ [rootlocalhost containers]# ls certs.d policy.json registries.conf.d storage.conf oci registries.conf re…...

计算机毕业设计SpringBoot+Vue.jst0甘肃非物质文化网站(源码+LW文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…...

使用Python脚本转换YOLOv5配置文件到https://github.com/ultralytics/ultralytics:一个详细的指南

在深度学习领域&#xff0c;YOLO&#xff08;You Only Look Once&#xff09;系列模型因其高效和准确性而广受欢迎。然而&#xff0c;随着项目需求的变化&#xff0c;有时我们需要对预训练模型的配置文件进行调整。本文将详细介绍如何使用Python脚本自动转换YOLOv5的配置文件到…...

简识Kafka集群与RocketMQ集群的核心区别

前记&#xff1a;各位潘安、各位子健/各位彦祖、于晏&#xff0c;文字较多&#xff0c;优先看目录。 Kafka集群与RocketMQ集群的核心区别及架构图例说明 一、核心区别对比 特性Kafka 集群RocketMQ 集群设计目标高吞吐量实时日志流系统&#xff08;如日志收集、大数据流水线&a…...

制造行业CRM选哪家?中大型企业CRM选型方案

在当今竞争激烈的制造行业中&#xff0c;企业对于客户关系管理&#xff08;CRM&#xff09;系统的需求日益增强&#xff0c;高效、智能的CRM系统已成为推动企业业务增长、优化客户体验的关键。在制造业 CRM 市场中&#xff0c;纷享销客和销售易都备受关注&#xff0c;且各自有着…...