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
<!-- 默认通过无参构造来创建 Date 实例 -->
<bean id="date" class="java.util.Date" />
<!-- 默认通过无参构造来创建 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
<!-- 通过 Sun 类的 getInstance() 方法来返回 Sun 实例 -->
<bean id="sun" class="io.malajava.ioc.beancreation.Sun" factory-method="getInstance" />
<!-- 通过调用 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 类型的 bean -->
<bean id="carFactory" class="io.malajava.ioc.beancreation.CarFactory" />
<!-- 指定 通过 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<Bus> {
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
<bean id="bus" class="io.malajava.ioc.beancreation.BusFactoryBean" >
<property name="name" value="比亚迪" />
</bean>
注: 设以上内容存在于类路径下的 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<Date> {
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
<bean id="date" class="io.malajava.ioc.beancreation.DateFactoryBean">
<property name="year" value="1996" />
<property name="month" value="7" />
<property name="date" value="4" />
<property name="hours" value="8" />
<property name="minutes" value="20" />
<property name="seconds" value="30" />
</bean>
注: 设以上内容存在于类路径下的 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 ) );
}
}