根据 【动力节点】最新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>