【Spring】Bean 的实例化(创建 | 获取)

简介: 【Spring】Bean 的实例化(创建 | 获取)

根据 【动力节点】最新Spring框架教程,全网首套Spring6教程,跟老杜从零学spring入门到高级 以及老杜的原版笔记 https://www.yuque.com/docs/share/866abad4-7106-45e7-afcd-245a733b073f?# 《Spring6》 进行整理, 文档密码:mg9b


Spring 相关文章整理汇总归纳于:https://www.yuque.com/u27599042/zuisie


  • Spring为Bean提供了多种实例化方式,包括如下4种方式:
  • 第一种:通过构造方法实例化
  • 第二种:通过简单工厂模式实例化
  • 第三种:通过factory-bean实例化(工厂方法模式实例化)
  • 第四种:通过FactoryBean接口实例化
  • Spring中为Bean对象的创建准备了多种方案,目的是为了能够更加灵活

通过构造方法实例化

  • 我们之前一直使用的就是这种方式。
  • 在spring配置文件中直接配置类全路径,即在Spring配置文件中配置bean,Spring会自动调用该类的无参数构造方法来实例化Bean
/**
 * ClassName: SpringBean
 * Package: cw.sspring.study.bean
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-25 18:10
 * @Version 1.0
 */
public class SpringBean {
}
<?xml version="1.0" encoding="UTF-8"?>
<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="springBean" class="cw.spring.study.bean.SpringBean"/>
</beans>
@org.junit.Test
public void test1() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    System.out.println(applicationContext.getBean("springBean", SpringBean.class));
}

通过简单工厂模式实例化

抽象产品类

/**
 * ClassName: Fruit
 * Package: cw.spring.study.bean
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-25 18:20
 * @Version 1.0
 */
public abstract class Fruit {}

具体产品类

/**
 * ClassName: Apple
 * Package: cw.spring.study.bean
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-25 18:20
 * @Version 1.0
 */
public class Apple extends Fruit{}

工厂类

/**
 * ClassName: FruitFactory
 * Package: cw.spring.study.bean
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-25 18:21
 * @Version 1.0
 */
public class FruitFactory {
    // 创建水果对象的方法
    public static Fruit getFruit() {
        // 最终的Apple类对象是通过调用该工厂类的该方法new的对象
        return new Apple();
    }
}

配置文件配置工厂类生产 bean

  • 在Spring配置文件中告诉Spring框架,调用哪个类的哪个方法获取 Bean
<!--
  factory-method属性
    指定的是工厂类当中的静态方法,也就是告诉Spring框架,调用这个方法可以获取Bean
  创建出来的为产品对象,不为工厂对象,工厂类中的方法为静态方法,通过类名直接调用创建对象
  通过class属性指定的类调用该类的静态工厂方法,进行对象的创建,
  最终创建的对象为产品对象,id为apple
-->
<bean id="apple" class="cw.spring.study.bean.FruitFactory" factory-method="getFruit"/>

测试

@org.junit.Test
public void test1() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    System.out.println(applicationContext.getBean("apple", Fruit.class));
}

通过factory-bean实例化(工厂方法模式实例化)

抽象产品类

/**
 * ClassName: Fruit
 * Package: cw.spring.study.bean
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-25 18:20
 * @Version 1.0
 */
public abstract class Fruit {}

具体产品类

/**
 * ClassName: Apple
 * Package: cw.spring.study.bean
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-25 18:20
 * @Version 1.0
 */
public class Apple extends Fruit{}

抽象工厂类

/**
 * ClassName: FruitFactory
 * Package: cw.spring.study.bean
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-25 18:21
 * @Version 1.0
 */
public abstract class FruitFactory {
    /**
     * 水果工厂生产水果的方法
     * 
     * @return 水果
     */
    public abstract Fruit getFruit();
}

具体工厂类

/**
 * ClassName: AppleFactory
 * Package: cw.spring.study.bean
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-25 18:37
 * @Version 1.0
 */
public class AppleFactory extends FruitFactory{
    // 实例方法
    @Override
    public Fruit getFruit() {
        // 生产苹果
        return new Apple();
    }
}

配置文件配置工厂类生产 bean

  • 告诉Spring框架,调用哪个对象的哪个方法来获取Bean
<!-- 
  工厂方法模式,通过 factory-bean属性 + factory-method属性来共同完成 
  由于工厂类用于创建对象的方法为实例方法,所以需要先创建工厂对象,工厂对象也需要被spring管理
  AppleFactory 实际上是一个工厂bean,用于创建对象的bean
-->
<bean id="appleFactory" class="cw.spring.study.bean.AppleFactory"/>
<!--
  factory-bean属性
    告诉Spring调用哪个工厂对象,进行bean的创建
  factory-method属性
    告诉Spring调用工厂对象的哪个实例方法创建bean
  该配置用于指定用哪个工厂对象的哪个方法创建对象
-->
<bean id="apple" factory-bean="appleFactory" factory-method="getFruit"/>

测试

@org.junit.Test
public void test1() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    System.out.println(applicationContext.getBean("apple", Fruit.class));
}

通过FactoryBean接口实例化

  • 以上的第三种方式中,factory-bean(生产bean的工厂bean)是我们自定义的,factory-method(生产bean的工厂方法)也是我们自己定义的。
  • 在Spring中,当我们编写的类直接实现FactoryBean接口之后,用于创建bean的factory-bean就不需要我们指定了,工厂bean的factory-method也不需要我们指定了。factory-bean会自动指向实现FactoryBean接口的类,factory-method会自动指向该类中的getObject()方法。
  • 因为我们在配置文件中指定的用于创建bean的类在实现FactoryBean接口之后,Spring就知道这是一个工厂类,并且知道工厂方法是哪个
  • 这种方式实际上就是第三种方式的简化
  • FactoryBean在Spring中是一个接口,被称为“工厂Bean”。
  • “工厂Bean”是一种特殊的Bean,所有的“工厂Bean”都是用来协助Spring框架来创建其他Bean对象的。

产品类

/**
 * ClassName: Person
 * Package: cw.spring.study.bean
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-25 18:56
 * @Version 1.0
 */
public class Person {}

工厂类

import org.springframework.beans.factory.FactoryBean;
/**
 * ClassName: PersonFactoryBean
 * Package: cw.spring.study.bean
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-25 18:57
 * @Version 1.0
 */
public class PersonFactoryBean implements FactoryBean<Person> {
    // 创建bean的工厂方法
    @Override
    public Person getObject() throws Exception {
        return new Person();
    }
    @Override
    public Class<?> getObjectType() {
        return null;
    }
}
public interface FactoryBean<T> {
    String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
    @Nullable
    T getObject() throws Exception;
    @Nullable
    Class<?> getObjectType();
    // 该方法接口进行了默认实现,工厂bean创建的对象是否为单例的
    // 默认情况下,创建的对象是单例的
    // 如果要让工厂bean创建的对象为多例,则重写该方法,返回false即可
    default boolean isSingleton() {
        return true;
    }
}

配置文件配置工厂类生产 bean

<!--
  这种方式实际上就是第三种方式的简化。
  由于你编写的类实现了FactoryBean接口,所以这个类是一个特殊的类,
  不需要你再手动指定:factory-bean、factory-method
  实现了FactoryBean接口,Spring知道这是一个工厂类,并且知道用于创建bean的工厂方法是哪个
  通过一个特殊的Bean:工厂Bean,来返回一个普通的Bean Person对象。
  通过FactoryBean这个工厂Bean主要是想对普通Bean进行加工处理。
-->
<bean id="person" class="cw.spring.study.bean.PersonFactoryBean"/>

测试

@org.junit.Test
public void test1() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    System.out.println(applicationContext.getBean("person", Person.class));
}

工厂方法实例化小结

  • 相比于通过构造方法进行bean的实例化,采用工厂方法进行bean的实例,我们可以在bean对象创建出来到将创建出来的对象交给Spring容器进行管理这一段时间内,我们可以对bean对象进行加工处理。

BeanFactory 和 FactoryBean 的区别

BeanFactory

  • BeanFactory Spring 容器的顶级父接口
  • Spring IoC容器的顶级对象,BeanFactory被翻译为“Bean工厂”,在Spring的IoC容器中,“Bean工厂”负责创建Bean对象,即BeanFactory是一个创建bean对象的工厂。
  • BeanFactory是工厂。

FactoryBean

  • FactoryBean:它是一个Bean,是一个能够辅助Spring实例化其它Bean对象的一个Bean。
  • 在Spring中,Bean可以分为两类:
  • 第一类:普通Bean
  • 第二类:工厂Bean,工厂Bean也是一种Bean,只不过这种Bean比较特殊,它可以辅助Spring实例化其它Bean对象,即工厂Bean是一种用于创建Bean对象的Bean。
  • 我们自己写的,用于实例化Bean对象的工厂Bean就是FactoryBean
  • 工厂Bean是一种特殊的Bean,用于获取其他Bean的一种特殊Bean

FactoryBean 实战练习 - 注入自定义 Date

  • java.util.Date在Spring中被当做简单类型,简单类型在注入的时候可以直接使用value属性或value标签来完成。
  • 但是对于Date类型来说,采用value属性或value标签赋值的时候,对日期字符串的格式要求非常严格,必须是这种格式的:Mon Oct 10 14:30:26 CST 2022。其他格式是不会被识别的。
/**
 * ClassName: Student
 * Package: cw.spring.study.bean
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-26 9:04
 * @Version 1.0
 */
public class Student {
    private Date birth;
    public void setBirth(Date birth) {
        this.birth = birth;
    }
    @Override
    public String toString() {
        return "Student{" + "birth=" + birth + '}';
    }
}

以简单类型的方式注入 Date

<bean id="student" class="cw.spring.study.bean.Student">
  <property name="birth" value="Mon Oct 10 14:30:26 CST 2022"/>
</bean>
@org.junit.Test
public void test2() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    System.out.println(applicationContext.getBean("student", Student.class));
}

  • 对于Date来说,我们也可以采用非简单类型的形式,但是如下这种方式创建出来的Date对象,默认是系统当前时间,也只能是系统当前时间
<!-- 采用这种形式创建出来的Date对象,默认是系统当前时间,也只能是系统当前时间 -->
<bean id="studentBirth" class="java.util.Date"/>

以非简单类型的方式通过 FactoryBean 接口实例化

  • 采用工厂方法模式以非简单类型的方式通过 FactoryBean 接口实例化,我们可以在工厂方法中创建Date对象,同时还可以对Date对象进行加工,从而创建我们需要格式的日期对象
/**
 * ClassName: DateFactoryBean
 * Package: cw.spring.study.bean
 * Description:
 * DateFactoryBean 这是一个工厂Bean,
 * 用于协助Spring创建Date对象
 *
 * @Author tcw
 * @Create 2023-05-26 9:13
 * @Version 1.0
 */
public class DateFactoryBean implements FactoryBean<Date> {
    private String dateStr; // 日期字符串(简单类型)
    public void setDateStr(String dateStr) {
        this.dateStr = dateStr;
    }
    @Override
    public Date getObject() throws Exception {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        return dateFormat.parse(dateStr);
    }
    @Override
    public Class<?> getObjectType() {
        return null;
    }
}
<!-- 利用工厂Bean DateFactoryBean 创建指定日期的Date对象 -->
<bean id="date" class="cw.spring.study.bean.DateFactoryBean">
  <property name="dateStr" value="2023-05-22"/>
</bean>
<bean id="student" class="cw.spring.study.bean.Student">
  <property name="birth" ref="date"/>
</bean>

相关文章
|
3月前
|
XML Java 数据格式
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
这篇文章是Spring5框架的实战教程,主要介绍了如何在Spring的IOC容器中通过XML配置方式使用外部属性文件来管理Bean,特别是数据库连接池的配置。文章详细讲解了创建属性文件、引入属性文件到Spring配置、以及如何使用属性占位符来引用属性文件中的值。
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
|
11天前
|
缓存 Java Spring
实战指南:四种调整 Spring Bean 初始化顺序的方案
本文探讨了如何调整 Spring Boot 中 Bean 的初始化顺序,以满足业务需求。文章通过四种方案进行了详细分析: 1. **方案一 (@Order)**:通过 `@Order` 注解设置 Bean 的初始化顺序,但发现 `@PostConstruct` 会影响顺序。 2. **方案二 (SmartInitializingSingleton)**:在所有单例 Bean 初始化后执行额外的初始化工作,但无法精确控制特定 Bean 的顺序。 3. **方案三 (@DependsOn)**:通过 `@DependsOn` 注解指定 Bean 之间的依赖关系,成功实现顺序控制,但耦合性较高。
实战指南:四种调整 Spring Bean 初始化顺序的方案
|
1月前
|
XML Java 数据格式
Spring从入门到入土(bean的一些子标签及注解的使用)
本文详细介绍了Spring框架中Bean的创建和使用,包括使用XML配置文件中的标签和注解来创建和管理Bean,以及如何通过构造器、Setter方法和属性注入来配置Bean。
66 9
Spring从入门到入土(bean的一些子标签及注解的使用)
|
2月前
|
缓存 安全 Java
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
从底层源码入手,通过代码示例,追踪AnnotationConfigApplicationContext加载配置类、启动Spring容器的整个流程,并对IOC、BeanDefinition、PostProcesser等相关概念进行解释
205 24
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
|
30天前
|
Java 测试技术 Windows
咦!Spring容器里为什么没有我需要的Bean?
【10月更文挑战第11天】项目经理给小菜分配了一个紧急需求,小菜迅速搭建了一个SpringBoot项目并完成了开发。然而,启动测试时发现接口404,原因是控制器包不在默认扫描路径下。通过配置`@ComponentScan`的`basePackages`字段,解决了问题。总结:`@SpringBootApplication`默认只扫描当前包下的组件,需要扫描其他包时需配置`@ComponentScan`。
|
2月前
|
XML Java 数据格式
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
Spring 第二节内容补充 关于Bean配置的更多内容和细节 万字详解!
210 18
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
|
2月前
|
XML Java 数据格式
spring复习02,xml配置管理bean
详细讲解了Spring框架中基于XML配置文件管理bean的各种方式,包括获取bean、依赖注入、特殊值处理、属性赋值、集合类型处理、p命名空间、bean作用域及生命周期和自动装配。
spring复习02,xml配置管理bean
|
1月前
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细解析Spring Bean的生命周期及其核心概念,并深入源码分析。Spring Bean是Spring框架的核心,由容器管理其生命周期。从实例化到销毁,共经历十个阶段,包括属性赋值、接口回调、初始化及销毁等。通过剖析`BeanFactory`、`ApplicationContext`等关键接口与类,帮助你深入了解Spring Bean的管理机制。希望本文能助你更好地掌握Spring Bean生命周期。
73 1
|
1月前
|
Java Spring
获取spring工厂中bean对象的两种方式
获取spring工厂中bean对象的两种方式
38 1
|
1月前
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细介绍了Spring框架中的核心概念——Spring Bean的生命周期,包括实例化、属性赋值、接口回调、初始化、使用及销毁等10个阶段,并深入剖析了相关源码,如`BeanFactory`、`DefaultListableBeanFactory`和`BeanPostProcessor`等关键类与接口。通过理解这些核心组件,读者可以更好地掌握Spring Bean的管理和控制机制。
84 1