0%

Bean的实例化方式

  • Bean的实例化方式
    • 通过构造方法实例化
    • 通过简单工厂模式实例化
    • 通过factory-bean实例化(工厂方法模式实例化)
    • 通过FactoryBean接口实例化

七、Bean的实例化方式

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

7.1 通过构造方法实例化

  • 我们之前一直使用的就是这种方式。

  • 默认情况下,会调用 Bean 的无参数构造方法。

  • 在 Spring 配置文件中直接配置类的全路径,即在 Spring 配置文件中配置 bean,Spring 会自动调用该类的无参数构造方法来实例化 Bean

    1
    2
    3
    4
    5
    6
    7
    8
    package com.f.spring6.bean;

    /**
    * @author fzy
    * @date 2024/1/20 16:40
    */
    public class SpringBean {
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <?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">
    <!--
    Spring提供的实例化方式第一种:通过构造方法实例化
    在Spring配置文件中直接配置类的全路径,Spring会自动调用该类的无参数构选方法米实例化Bean
    -->
    <bean id="springBean" class="com.f.spring6.bean.SpringBean"/>
    </beans>
    1
    2
    3
    4
    5
    6
    @Test
    public void testInstantiation1() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    SpringBean springBean = applicationContext.getBean("springBean", SpringBean.class);
    System.out.println(springBean);
    }

7.2 通过简单工厂模式实例化

1
2
3
4
5
6
7
8
9
10
package com.f.spring6.bean;

/**
* 具体产品角色
*
* @author fzy
* @date 2024/1/20 16:46
*/
public class Star {
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.f.spring6.bean;

/**
* 简单工厂模式中的工厂类角色
*
* @author fzy
* @date 2024/1/20 16:46
*/
public class StarFactory {
// 静态方法
public static Star get() {
// 实际上这个Star对象最终创建的时候还是我们负责new的。
return new Star();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
<?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">
<!--
Spring提供的实例化方式第二种:通过简单工厂模式实例化
你需要在Spring配置文件中告诉Spring框架,调用哪个类的什么方法获取Bean
-->
<!--factory-method 属性指定的是工厂类当中的静态方法,也就是告诉Spring框架,调用这个方法来获取bean-->
<!--创建出来的为产品对象,不为工厂对象,工厂类中的方法为静态方法,通过类名直接调用该静态方法,创建对象-->
<bean id="star" class="com.f.spring6.bean.StarFactory" factory-method="get"/>
</beans>
1
2
3
4
5
6
@Test
public void testInstantiation2() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Star star = applicationContext.getBean("star", Star.class);
System.out.println(star);
}

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

1
2
3
4
5
6
7
8
9
10
11
package com.f.spring6.bean;

/**
* 工厂方法模式中的:抽象产品角色
*
* @author fzy
* @date 2024/1/20 21:00
*/
public abstract class Fruit {
public abstract void taste();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.f.spring6.bean;

/**
* 工厂方法模式中的:具体产品角色
*
* @author fzy
* @date 2024/1/20 21:01
*/
public class Apple extends Fruit {
@Override
public void taste() {
System.out.println("苹果尝起来是甜的");
}
}
1
2
3
4
5
6
7
8
9
10
11
package com.f.spring6.bean;

/**
* 工厂方法模式中的:抽象工厂角色
*
* @author fzy
* @date 2024/1/20 21:00
*/
public abstract class FruitFactory {
public abstract Fruit getFruit();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.f.spring6.bean;

/**
* 工厂方法模式中的:具体工厂角色
*
* @author fzy
* @date 2024/1/20 21:02
*/
public class AppleFactory extends FruitFactory {
@Override
public Fruit getFruit() {
// 实际上这个对象还是我们自己new的,不是Spring容器帮我们创建的
return new Apple();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
<?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">
<!--
Spring提供的实例化方式第三种:通过工厂方法模式实例化
通过factory-bean属性 + factory-method属性来共同完成
factory-bean属性:告诉Spring调用哪个工厂对象,进行bean的创建
factory-method属性:告诉Spring调用工厂对象的哪个实例方法创建bean
-->
<bean id="apple" factory-bean="appleFactory" factory-method="getFruit"/>
<bean id="appleFactory" class="com.f.spring6.bean.AppleFactory"/>
</beans>
1
2
3
4
5
6
@Test
public void testInstantiation3() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Apple apple = applicationContext.getBean("apple", Apple.class);
apple.taste();
}

7.4 通过FactoryBean接口实例化

  • 以上的第三种方式中,factory-bean(生产 bean 的工厂 bean)是我们自定义的,factory-method(生产 bean 的工厂方法)也是我们自己定义的。

  • 在 Spring 中,当我们编写的类直接实现 FactoryBean 接口之后,用于创建 beanfactory-bean 就不需要我们指定了,工厂 beanfactory-method 也不需要我们指定了。

    factory-bean 会自动指向实现 FactoryBean 接口的类,factory-method 会自动指向该类中的 getObject() 方法。

    • 因为我们在配置文件中指定的用于创建 bean 的类在实现 FactoryBean 接口之后,Spring 就知道这是一个工厂类,并且知道用于创建 bean 的工厂方法是哪个。

    第四种方式实际上就是第三种方式的简化。

  • FactoryBean 在 Spring 中是一个接口,被称为 “工厂Bean”。

    • “工厂Bean” 是一种特殊的 Bean,所有的 “工厂Bean” 都是用来协助 Spring 框架来创建其他 Bean 对象的。
1
2
3
4
5
6
7
8
package com.f.spring6.bean;

/**
* @author fzy
* @date 2024/1/20 21:22
*/
public class Person {
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.f.spring6.bean;

import org.springframework.beans.factory.FactoryBean;

/**
* @author fzy
* @date 2024/1/20 21:23
*/
public class PersonFactoryBean implements FactoryBean<Person> {
@Override
public Person getObject() throws Exception {
// 最终这个Bean的创建还是程序员自己new的
return new Person();
}

@Override
public Class<?> getObjectType() {
return Person.class;
}

// 这个方法在接口中有默认实现
// 默认返回true,表示单例
// 如果想多例,直接将这个方法的返回值修改为false即可
@Override
public boolean isSingleton() {
return true;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?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">
<!--
Spring提供的实例化方式第四种:通过FactoryBean接口实例化
这种方式实际上是第三种方式的简化
由于你编写的类实现了FactoryBean接口,所以这个类是一个特殊的类,不需要你再手动指定:factory-bean、factory-method
-->
<!--
通过一个特殊的工厂Bean:PersonFactoryBean
来返回一个普通的Bean:Person对象
-->
<bean id="person" class="com.f.spring6.bean.PersonFactoryBean"/>
</beans>
1
2
3
4
5
6
@Test
public void testInstantiation4() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Person person = applicationContext.getBean("person", Person.class);
System.out.println(person);
}
  • 相比于通过构造方法进行 bean 的实例化(第一种方式),采用工厂方法进行 bean 的实例化(第二、三、四种方式),可以使我们在 bean 对象创建出来到将创建出来的对象交给 Spring 容器进行管理这一段时间内,对 bean 对象进行加工处理

7.5 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
      • FactoryBeanBean

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

  • java.util.Date 在 Spring 中被当做简单类型,简单类型在注入的时候可以直接使用 value 属性或 value 标签来完成。

    • 但是对于 Date 类型来说,采用 value 属性或 value 标签赋值的时候,对日期字符串的格式要求非常严格,必须是这种格式的:Mon Oct 10 14:30:26 CST 2022。其他格式是不会被识别的。
  • 采用工厂方法模式可以让我们以非简单类型的方式,通过 FactoryBean 接口实例化 Date 对象,同时还可以对 Date 对象进行加工,从而不需要严格按照日期字符串的格式要求,也能创建出日期对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    package com.f.spring6.bean;

    import java.util.Date;

    /**
    * @author fzy
    * @date 2024/1/20 21:44
    */
    public class Student {
    private Date birth;

    public void setBirth(Date birth) {
    this.birth = birth;
    }

    @Override
    public String toString() {
    return "Student{" +
    "birth=" + birth +
    '}';
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    package com.f.spring6.bean;

    import org.springframework.beans.factory.FactoryBean;

    import java.text.SimpleDateFormat;
    import java.util.Date;

    /**
    * @author fzy
    * @date 2024/1/20 21:47
    */
    public class DateFactoryBean implements FactoryBean<Date> {
    // DateFactoryBean这个工厂Bean协助Spring创建普通的Bean:Date
    private String strDate;

    public DateFactoryBean(String strDate) {
    this.strDate = strDate;
    }

    @Override
    public Date getObject() throws Exception {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    Date date = sdf.parse(strDate);
    return date;
    }

    @Override
    public Class<?> getObjectType() {
    return Date.class;
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <?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="student" class="com.f.spring6.bean.Student">
    <property name="birth" ref="date"/>
    </bean>
    <!--通过DateFactoryBean这个工厂Bean来帮助我们创建Date Bean-->
    <bean id="date" class="com.f.spring6.bean.DateFactoryBean">
    <!--只要按yyyy-MM-dd格式输入,就能创建Date对象,因为我们已经在DateFactoryBean中定义了-->
    <constructor-arg name="strDate" value="2000-10-10"/>
    </bean>
    </beans>
    1
    2
    3
    4
    5
    6
    @Test
    public void testInstantiation5() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    Student student = applicationContext.getBean("student", Student.class);
    System.out.println(student);
    }
    • DateFactoryBeanDate 的过程为:

      • 通过构造方法创建 DateFactoryBean 这个工厂 Bean,并且根据传入的参数

        <constructor-arg name="strDate" value="2000-10-10"/>

        DateFactoryBeanstrDate 赋值。

      • Spring 容器会调用工厂 Bean 的 getObject() 方法,在这个方法中,我们根据 strDate 创建 Date 对象。

      • DateFactoryBean 所创建的 Date 对象即为赋给 Student 对象的 Date Bean。

---------------The End---------------