SpringFramework实例化 Bean ( Instantiating beans ) 的四种方式。

本文详细介绍了Spring IoC容器中Bean的四种实例化方式:构造方法实例化、静态工厂方法实例化、实例工厂方法实例化以及通过FactoryBean自定义实例化逻辑。每种方式均通过具体示例进行说明。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本节主要讲解在 Spring IoC 容器中实例化 Bean ( Instantiating beans ) 的四种方式。

由于英文水平有限,同时为保证原汁原味地表达Spring中关于Bean实例化部分本来的含义,标题和部分内容采用 Spring Framework 官方文档中的英文原文。

另外,在测试代码中读取 Spring 配置文件时,尽量使用了完整的路径而未使用通配符。

1、Instantiation with a constructor
【 通过一个构造方法来实例化 Bean 】

Java Bean
package io.malajava.ioc.beancreation;

import java.util.Date;

public class Human {

private Integer id ; // 对象标识(zhi)符字段 ( Object Identifier Field )
private String name ; // 用来保存姓名的字段
private char gender ; // 用来保存性别的字段
private Date birthdate ; // 用来保存 出生年月 对应的 Date 对象
private boolean married ; // 用来表示是否已婚的字段 ( true 表示已婚 ;false 表示未婚 )

public Human() {
    super();
}

public Human(Integer id, String name, char gender) {
    super();
    this.id = id;
    this.name = name;
    this.gender = gender;
}

/* 此处省略 所有 字段 对应的 getter 和 setter 方法 ,若要运行程序,请自行添加 */

/* 建议重写 该类的 toString 方法,并在其中输出 各个字段的值 */

}

Configuration Metadata

<!-- 默认通过无参构造来创建 Human 实例 -->
<bean id="firstHuman" class="io.malajava.ioc.beancreation.Human">
    <!-- 通过 property 为 Human 实例的各个属性注入数值 ( 通过 setter 实现注入 ) -->
    <property name="id" value="1001" />
    <property name="name" value="杨过" />
    <property name="gender" value="男" />
    <property name="married" value="false" />
    <property name="birthdate" ref="date" />
</bean>

<!-- 在 bean 标签内部 使用  constructor-arg 指定构造方法的参数,从而可以调用带有指定参数列表的构造方法 -->
<bean id="secondHuman" class="io.malajava.ioc.beancreation.Human">
    <!-- constructor 表示 构造方法 ; arg 是 arguments 的缩写,表示参数 -->
    <constructor-arg name="id" value="2002" />
    <constructor-arg name="name" value="小龙女"/>
    <constructor-arg name="gender" value="女" />
</bean>

注: 设以上内容存在于类路径下的 io/malajava/ioc/beancreation/creation-via-constructor.xml 文件中

Testing
public class CreationViaConstructorTest {

public static void main(String[] args) {

    // 指定 Configuration Metadata
    String configLocation = "classpath:io/malajava/ioc/beancreation/creation-via-constructor.xml" ;

    // 创建 Spring IoC 容器
    ApplicationContext iocContainer = new ClassPathXmlApplicationContext( configLocation ) ;

    // 从容器中获取 指定 id 对应的 bean , 并明确其类型
    Human first = iocContainer.getBean( "firstHuman" , Human.class );
    System.out.println( first ); // first.toString()

    Human second = iocContainer.getBean( "secondHuman" , Human.class );
    System.out.println( second ); // second.toString()

}

}
2、Instantiation with a static factory method
【 通过一个静态工厂方法来实例化 Bean 】

Java Bean
package io.malajava.ioc.beancreation;

/** 单例模式 ( 饿汉式 ) */
public class Sun {

// 在 Sun 类内部创建该类的实例 ( 自己内部创建自己的实例 )
private static final Sun SUN = new Sun();

// 将构造方法私有化,避免在 Sun 类外部创建 Sun 实例
private Sun(){
    super();
}

// 提供一个 类方法 用来返回 Sun 类的惟一实例
public static Sun getInstance() {
    return SUN  ;
}

}
Configuration Metadata

<!-- 通过调用  Calendar 类的 getInstance() 方法返回 Calendar 实例 -->
<bean id="calendar" class="java.util.Calendar" factory-method="getInstance" />

这里,除了采用静态工厂方式返回Sun实例外,特别增加了一个采用静态工厂方法返回Calendar实例的配置,两者在返回实例的方式上是相同的(都使用静态方法)。不同的是我们的Sun类采用了单例模式,而Calendar类并没有采用单例模式。

另外请不要将单例模式跟 scope 中的 singleton 相混淆。

注:设以上内容存在于类路径下的 io/malajava/ioc/beancreation/creation-via-static-factory.xml 文件中

Testing
public class CreationViaStaticFactoryTest {

public static void main(String[] args) {

    // 指定 Configuration Metadata
    String configLocation = "classpath:io/malajava/ioc/beancreation/creation-via-static-factory.xml" ;

    // 创建 Spring IoC 容器
    ApplicationContext iocContainer = new ClassPathXmlApplicationContext( configLocation ) ;

    // 从容器中获取 指定 id 对应的 bean ,并明确其类型
    Calendar c = iocContainer.getBean( "calendar" , Calendar.class );
    System.out.println( c );

    Sun s = iocContainer.getBean( "sun" , Sun.class );
    System.out.println( s );

}

}
3、Instantiation using an instance factory method
【 使用一个实例工厂方法来实例化 Bean 】

Java Bean
package io.malajava.ioc.beancreation;

public class Car {

private String brand ;

public Car() {
    super();
}

public String getBrand() {
    return brand;
}

public void setBrand(String brand) {
    this.brand = brand;
}

}
Factory
注意这里的 CarFactory 也是一个 Java Bean ,为了强调实例工厂,所以单独列出来。

package io.malajava.ioc.beancreation;

import java.util.Random;

public class CarFactory {

private final String[] brands = { "秦" , "汉" , "唐" , "宋" ,  "元" , "明"  } ;
private final Random rand = new Random();

public Car produce() {
    Car c = new Car();
    int index = rand.nextInt( brands.length ) ;
    String b = brands[ index ] ;
    c.setBrand(  b );
    return c ;
}

}

Configuration Metadata

<!-- 指定 通过  carFactory 的  produce 方法 来创建 Car 类型的 bean -->
<bean id="car" factory-bean="carFactory" factory-method="produce" />

注: 设以上内容存在于类路径下的 io/malajava/ioc/beancreation/creation-via-instance-factory.xml 文件中

Testing
public class CreationViaInstanceFactoryTest {

public static void main(String[] args) {

    // 指定 Configuration Metadata
    String configLocation = "classpath:io/malajava/ioc/beancreation/creation-via-instance-factory.xml" ;

    // 创建 Spring IoC 容器
    ApplicationContext iocContainer = new ClassPathXmlApplicationContext( configLocation ) ;

    // 从容器中获取 指定 id 对应的 bean , 并明确其类型
    Car c = iocContainer.getBean( "car" , Car.class );
    System.out.println( c ); // c.toString()

    System.out.println( c.getBrand() );

}

}
4、Customizing instantiation logic with a FactoryBean
【 通过FactoryBean实现自定义实例化逻辑 】

4.1、理解使用FactoryBean方式创建实例的过程
Java Bean
package io.malajava.ioc.beancreation;

public class Bus {

private String brand ; // 表示"铭牌"的字段

public Bus() {
    super();
    System.out.println( "[ Bus ] - [ public Bus() ]" );
}

public String getBrand() {
    return brand;
}

public void setBrand(String brand) {
    System.out.println( "[ Bus ] - [ public void setBrand(String) ]" );
    this.brand = brand;
}

}

FactoryBean
package io.malajava.ioc.beancreation;

import org.springframework.beans.factory.FactoryBean;

/** 实现 FactoryBean 接口并指定 类型参数 为 Bus */
public class BusFactoryBean implements FactoryBean {

private String name ;

public BusFactoryBean() {
    super();
    System.out.println( "[ BusFactoryBean ] - [ public BusFactoryBean() ]");
}

@Override
public Bus getObject() throws Exception {
    System.out.println( "[ BusFactoryBean ] - [ public Bus getObject() throws Exception ]" );
    Bus bus = new Bus();
    bus.setBrand( name );
    return bus ;
}

@Override
public Class<?> getObjectType() {
    return Bus.class ; // 与 getObject() 方法的返回类型相同
}

public String getName() {
    return name;
}

public void setName(String name) {
    System.out.println( "[ BusFactoryBean ] - [ public void setName(String) ]" );
    this.name = name;
}

}

Configuration Metadata


注: 设以上内容存在于类路径下的 io/malajava/ioc/beancreation/creation-via-factory-bean.xml 文件中

Testing
public class CreationViaFactoryBeanTest1 {

public static void main(String[] args) {

    // 指定 Configuration Metadata
    String configLocation = "classpath:io/malajava/ioc/beancreation/creation-via-factory-bean.xml" ;

    // 创建 Spring IoC 容器
    ApplicationContext iocContainer = new ClassPathXmlApplicationContext( configLocation ) ;

    // 从容器中获取 指定 id 对应的 bean
    Object o = iocContainer.getBean( "bus" ); // 未指定类型
    System.out.println( o );
    System.out.println( o.getClass() );

    Bus bus = iocContainer.getBean( "bus" , Bus.class ); // 指定类型
    System.out.println( bus.getBrand() );

}

}
通过运行 CreationViaFactoryBeanTest1 类的 main 方法可以看到以下输出:

[ BusFactoryBean ] - [ public BusFactoryBean() ]
[ BusFactoryBean ] - [ public void setName(String) ]
[ BusFactoryBean ] - [ public Bus getObject() throws Exception ]
[ Bus ] - [ public Bus() ]
[ Bus ] - [ public void setBrand(String) ]
io.malajava.ioc.beancreation.Bus@503d687a
class io.malajava.ioc.beancreation.Bus
比亚迪
由以上输出可知,Spring IoC 容器先根据配置文件中的内容将 BusFactoryBean 实例化,并为该实例注入相关属性的值(如setName)。随后调用该实例的 getObject() 方法来获取 Bus 实例。

而在 BusFactoryBean 中显式调用了 Bus 类的构造 和 setBrand 方法,因此可以看到以下输出:

[ Bus ] - [ public Bus() ]
[ Bus ] - [ public void setBrand(String) ]
至此,我们基本了解了使用 FactoryBean 方式来实例化 Bean 的步骤。

4.2、利用FactoryBean方式来创建任意时刻对应的Date对象
Java Bean
因为这里使用的是 java.util.Date 类,因此无需再定义新的类。
FactoryBean
package io.malajava.ioc.beancreation;

import org.springframework.beans.factory.FactoryBean;

import java.util.Calendar;
import java.util.Date;

public class DateFactoryBean implements FactoryBean {

private final Calendar calendar = Calendar.getInstance();

private int year ; // 表示年份的字段,比如 2018
private int month ; // 表示月份的字段,取值范围是 [ 1  , 12 ]
private int date ;  // 表示日期的字段,取值范围是 [ 1  , 31 ]
private int hours ; // 表示小时的字段,取值范围是 [ 0 , 23 ]
private int minutes ; // 表示分钟的字段,取值范围是 [ 0 , 59 ]
private int seconds ; // 表示秒数的字段,取值范围是 [ 0 , 60 ]

@Override
public Date getObject() throws Exception {
    calendar.clear(); // 清除所有 日历字段 ( Calendar Field ) 的值
    calendar.set( year , month - 1 , date , hours , minutes , seconds );
    Date date = calendar.getTime() ;
    return date ;
}

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

/*
此处省略所有字段的 getter 和 setter ,若要运行测试代码,请自行补全。

另外,应该在 setter 中对取值范围予以检查,
比如 分钟的取值范围是 [0,59] ,则其 setter 应该采用以下方式来实现:

public void setMinutes(int minutes) {
    if( minutes >= 0 && minutes <= 50 ) { // 使用判断保证数据有效性
        this.minutes = minutes;
    }
}

*/

}
Configuration Metadata







注: 设以上内容存在于类路径下的 io/malajava/ioc/beancreation/creation-via-factory-bean.xml 文件中

Testing
public class CreationViaFactoryBeanTest {

public static void main(String[] args) {

    // 指定 Configuration Metadata
    String configLocation = "classpath:io/malajava/ioc/beancreation/creation-via-factory-bean.xml" ;

    // 创建 Spring IoC 容器
    ApplicationContext iocContainer = new ClassPathXmlApplicationContext( configLocation ) ;

    // 从容器中获取 bean
    Date date = iocContainer.getBean( "date" , Date.class );
    System.out.println( date );

    DateFormat df = new SimpleDateFormat( "yyyy-MM-dd HH??ss" );
    System.out.println( df.format( date ) );

}

}
© 著作权归作者所有

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值