【Spring篇】Bean实例化的四种方式及应用案例

news/2023/11/30 9:43:34

目录

Bean的实例化

通过构造方法实例化Bean;

通过简单工厂模式创建对象;

使用案例

通过使用工厂方法模式

使用案例

通过FactoryBean接口实例化

使用案例


Bean的实例化

Bean的实例化有四种方式,这里的实例化Bean和之前学的注入是两回事,

依赖注入有两种方式,一种是构造方法注入另一种是set方法注入,这里的注入是指给类方法中的属性赋值,我们可以在构造方法运行的同时通过含参数构造方法注入属性的值,也可以在对象创建之后通过set方法给对象的属性赋值;这里创建对象是通过反射技术

然而实例化是创建对象也就是new对象,在Spring中提供的可以创建对象的方式有四种

通过构造方法实例化Bean;

这个方法是最开始接触Spring时使用的创建对象的方式,只需要在Spring.xml文件配置Bean标签中配置,然后在test方法中直接通过ApplicationContext调用getbean()方法;这是在底层调用了反射技术创建对象;

     <bean id="userBean" class="com.lypcode.spring.bean.user"/>

通过简单工厂模式创建对象;

这个方式是提供了一个工厂类,通过工厂类来间接获取对象,工厂类中会提供一个静态方法;然后通过工厂类中的静态方法创建Bean对象,相当于给了一个专门创建对象的工厂一样;但是需要再spring.xml文件中的Bean声明中指出factory-method属性;

 <bean id="vipBean" class="com.powernode.spring6.bean.VipFactory" factory-method="get"/>

这种方式:是在Spring容器读取xml文件时,Spring容器根据配置的Bean检测到,这个类配置了Factory-method属性是个工厂类,那么Spring容器会在创建类时调用class对应的这个类中的factory-method指定的静态方法来实例化Bean;其中工厂类的构造方法不执行  

 MyBean myBean = MyBeanFactory.createInstance();

这样做的好处是;它允许你在创建Bean的过程中执行更复杂的逻辑,例如根据条件返回不同的实例;

如何理解:既然他叫工厂类,呢么显然工厂不可能只生产一个东西,它是可以生产同一系列多种多样的产品,应用到实际需求中,会根据你请求参数的不同给你返回不同的对象,这里主要理解,工厂类起加工作用,或者说可以将被创建类加工成不同功能的类的一个功能;

使用案例

 public class MyBeanFactory {public static MyBean createInstance(String type) {if ("type1".equals(type)) {return new Type1Bean();} else if ("type2".equals(type)) {return new Type2Bean();} else {throw new IllegalArgumentException("Unsupported type: " + type);}}}​public class Type1Bean implements MyBean {// Type1Bean implementation}​public class Type2Bean implements MyBean {// Type2Bean implementation}

在这个例子中,MyBeanFactorycreateInstance 方法接受一个参数 type,根据不同的 type 返回不同类型的 MyBean 实例。你可以根据实际需求扩展这个逻辑,例如从配置文件中读取参数,执行一些计算,或者根据其他条件动态选择要返回的Bean类型。

 <bean id="myBean" class="com.example.MyBeanFactory" factory-method="createInstance"><constructor-arg value="type1"/></bean>

这样,当Spring容器需要实例化myBean时,它会调用MyBeanFactory类的createInstance方法,并传递"type1"作为参数,从而获取并返回相应的Type1Bean实例。

通过使用工厂方法模式

这种方法中是摒弃了之前的Bean中定义class属性,而是定义了一个Factory-Bean,这个就是工厂类,和之前不同的是,这个工厂类中提供的方法是非静态方法;这个Factory-Bean又指向另一个Bean标签,这个被指向的的类则作为工厂类;与简单工厂类实例化Bean方式相比较:这个过程中工厂类的无参构造会执行;

  这种方式利用另一个 Bean 的实例作为工厂来创建目标 Bean, 当 Spring 容器启动并读取配置文件时,它会实例化配置的工厂 Bean。当需要创建 myBean 时,容器将调用 myBeanFactory 的 createInstance 方法,而不是直接调用 MyBean 的构造方法创建MyBean对象。

使用 factory-bean 的方式,你可以借助现有的 Bean 实例作为工厂来创建其他 Bean 实例。这种方法适用于在实例化过程中可能需要其他实例或外部资源的情况;

 <bean id="orderFactory" class="com.lypcode.spring.bean.orderFactory"/><bean id="orderBean" factory-bean="orderFactory" factory-method="get"/>

使用案例

假设你的目标是在实例化过程中根据某些条件选择不同的实例化逻辑,而这些条件可能需要从外部环境中获取。例如,你可能有一个数据库连接工厂,根据配置中的数据库类型创建不同的数据库连接。

  1. 定义工厂 Bean: 创建一个工厂 Bean,该工厂可能需要一些外部条件。

     public class DatabaseConnectionFactory {private String databaseType;​public DatabaseConnectionFactory(String databaseType) {this.databaseType = databaseType;}​public DatabaseConnection createConnection() {// 根据数据库类型创建不同的连接实例if ("mysql".equals(databaseType)) {return new MySQLConnection();} else if ("oracle".equals(databaseType)) {return new OracleConnection();} else {throw new IllegalArgumentException("Unsupported database type: " + databaseType);}}}
  2. 在 Spring 配置文件中配置工厂 Bean: 在配置文件中定义工厂 Bean,传递外部条件。

     <bean id="databaseConnectionFactory" class="com.example.DatabaseConnectionFactory"><constructor-arg value="mysql"/> <!-- 可以从配置文件中读取 --></bean>
  3. 配置使用 factory-bean 实例化目标 Bean: 配置目标 Bean,使用 factory-bean 指定工厂 Bean 的 ID,并通过 factory-method 指定工厂方法。

     <bean id="databaseConnection" factory-bean="databaseConnectionFactory" factory-method="createConnection"/>

在这个例子中,当容器需要创建 databaseConnection 时,它将调用 databaseConnectionFactorycreateConnection 方法,根据外部条件(数据库类型)选择不同的连接实例。这种方式提供了更大的灵活性,允许你根据外部条件动态选择实例化逻辑。

通过FactoryBean接口实例化

以上的第三种方式中,factory-bean是我们自定义的,factory-method也是我们自己定义的。在Spring中,当你编写的类直接实现FactoryBean接口之后,factory-bean不需要指定了,factory-method也不需要指定了。factory-bean会自动指向实现FactoryBean接口的类,factory-method会自动指向getObject()方法。

在Spring中,通过工厂模式方法实例化Bean通常是通过使用工厂方法模式和Spring的工厂bean(FactoryBean)接口来实现的。下面是底层原理的简要解释:

  1. 定义工厂接口或类: 创建一个接口或类,定义工厂方法(工厂接口)或普通方法(工厂类),用于创建和返回Bean实例。这个接口或类可以是普通的Java类,也可以实现Spring的FactoryBean接口。

     public interface MyBeanFactory {MyBean createInstance();}
  2. 实现工厂接口或类: 创建一个实现了工厂接口或类的具体实现,实现工厂方法来创建Bean实例。

     public class MyBeanFactoryImpl implements MyBeanFactory {@Overridepublic MyBean createInstance() {return new MyBean();}}
  3. 在Spring配置文件中配置FactoryBean: 使用<bean>标签配置工厂bean,指定class属性为工厂实现类,Spring容器在需要创建Bean时会调用工厂方法来获取Bean实例。

    <bean id="myBeanFactory" class="com.example.MyBeanFactoryImpl"/>
  4. 配置使用FactoryBean创建Bean: 在配置文件中配置实际的Bean,使用factory-bean属性指定工厂bean的ID,使用factory-method属性指定工厂方法。

     <bean id="myBean" factory-bean="myBeanFactory" factory-method="createInstance"/>

    这告诉Spring容器在需要创建myBean Bean时,调用myBeanFactorycreateInstance方法来获取Bean实例。

  5. Spring容器实例化Bean: 当Spring容器启动并读取配置文件时,它将根据配置实例化工厂bean和实际的Bean。当需要创建myBean时,它将调用myBeanFactorycreateInstance方法,而不是直接调用Bean的构造方法。

使用案例

注入Date日期类

 public class Student {private Date birth;​public void setBirth(Date birth) {this.birth = birth;}​@Overridepublic String toString() {return "Student{" +"birth=" + birth +'}';}}

java.util.Date在Spring中被当做简单类型,简单类型在注入的时候可以直接使用value属性或value标签来完成。但我们之前已经测试过了,对于Date类型来说,采用value属性或value标签赋值的时候,对日期字符串的格式要求非常严格,必须是这种格式的:Mon Oct 10 14:30:26 CST 2022。其他格式是不会被识别的;

public class DateFactoryBean implements FactoryBean<Date> {​// 定义属性接收日期字符串private String date;​// 通过构造方法给日期字符串属性赋值public DateFactoryBean(String date) {this.date = date;}​@Overridepublic Date getObject() throws Exception {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");return sdf.parse(this.date);}​@Overridepublic Class<?> getObjectType() {return null;}}​

 <bean id="dateBean" class="com.powernode.spring6.bean.DateFactoryBean"><constructor-arg name="date" value="1999-10-11"/></bean>​<bean id="studentBean" class="com.powernode.spring6.bean.Student"><property name="birth" ref="dateBean"/></bean>

如果不使用FactoryBean,那么根据xml文件中的配置,直接使用注入外部Bean的方式, 就是让Spring容器管理并返回对象new java.util.Date,该对象使用的是默认的构造方法进行初始化,所以它的初始值是系统当前的时间,使用FactoryBean相当于可以在返回对象之前,对这个对象进行加工处理, 这样的话,就可以先使用注入简单类型的方法,先给FactoryBean注入strDate的值,然后在重写的getObject()方法内将Date类型的值改为自定义的时间,再将其返回。


https://www.xjx100.cn/news/3091941.html

相关文章

C++中的内存管理

✨前言✨ &#x1f4d8; 博客主页&#xff1a;to Keep博客主页 &#x1f646;欢迎关注&#xff0c;&#x1f44d;点赞&#xff0c;&#x1f4dd;留言评论 ⏳首发时间&#xff1a;2023年11月21日 &#x1f4e8; 博主码云地址&#xff1a;博主码云地址 &#x1f4d5;参考书籍&…

为什么云游戏被认为是行业的未来趋势?

5G 时代的到来&#xff0c;游戏行业也正在经历着一场革命性的变革。云游戏&#xff0c;这个看似神秘的新兴领域&#xff0c;正在逐渐成为行业的未来趋势。 一、云游戏的优势 摆脱硬件束缚 在传统游戏中&#xff0c;玩家需要购买昂贵的游戏主机或电脑&#xff0c;才能享受高质…

如何优雅的删除HashMap元素

文章目录 1.数据准备2.删除方式2.1.使用增强 for 循环删除2.2.使用 forEach 循环删除2.3.使用 Iterator 迭代器删除2.4 使用 removeIf 删除&#xff08;推荐使用&#xff09;2.5.使用 Stream 删除&#xff08;推荐使用&#xff09; 1.数据准备 public Map<String, String&g…

面向对象程序设计1-类的定义和使用

第1关&#xff1a;数字时钟走字 任务描述 本关任务&#xff1a;本题中已给出一个时钟类的定义&#xff0c;请模拟数字时钟走字过程。 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a;1.类和对象。 编程要求 根据提示&#xff0c;在右侧编辑器补充代码&…

substring-after用法

substring-after&#xff1a;函数返回一个字符串&#xff0c;该字符串是给定子字符串后给定字符串的其余部分。 #句法 substring-after( haystack ,needle) haystack&#xff1a;要评估的字符串。该字符串的一部分将被返回。 needle&#xff1a;要搜索的子字符串。needle在h…

js实现页面滚动时自动切换Sidebar标签,点击标签自动滚动页面

js实现页面滚动时自动切换Sidebar侧边导航标签&#xff0c;点击标签自动滚动页面 <van-sidebar class"sidebar" v-model"activeKey"><van-sidebar-item :title"i.title" click"onChange(i)" v-for"(i,k) in activeList&…

关于校园网使用罗技flow功能

目录 情况概述问题及解决方案 情况概述 我目前设备是一台Macbook air m1处理器&#xff0c;学校给配了一台windows台式&#xff0c;台式机不能连蓝牙&#xff0c;不能连wifi&#xff0c;只能用网线&#xff0c;我的需求是想让mac和windows共用一套键鼠&#xff0c;在了解到罗技…

magisk 手机卡重启界面记录

中午发现手机自动关机了&#xff0c;开机后无限卡重启界面&#xff0c;但是可以进 rec 和 fastboot。怀疑是之前安装的 magisk 模块导致卡重启。不过值得一提的是&#xff0c;虽然自动关机的问题此前也出现过几次&#xff0c;但是都没有出现过卡重启的情况。 解决问题 进 rec …