7.7.2 使用静态工厂方法创建Bean

7.7.2 使用静态工厂方法创建Bean

使用静态工厂方法创建Bean实例时, class属性也必须指定,但此时class属性并不是指定Bean实例的实现类,而是静态工厂类, Spring通过该属性知道由哪个工厂类来创建Bean实例。
除此之外,还需要使用factory-method属性来指定静态工厂方法, Spring将调用静态工厂方法(可能包含一组参数)返回一个Bean实例,一旦获得了指定Bean实例, Spring后面的处理步骤与采用普通方法创建Bean实例则完全一样。
下面的Bean要由factory-method指定的静态工厂方法来创建,所以这个<bean>元素的class属性指定的是静态工厂类, factory-method指定的工厂方法必须是静态的。
由此可见,采用静态工厂方法创建Bean实例时,<bean>元素需要指定如下两个属性:

属性 描述
class 该属性的值设置为静态工厂类的类名.
factory- method 该属性指定静态工厂方法来生产Bean实例。

如果静态工厂方法需要参数,则使用<constructor-arg>元素传入。

程序示例

项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
E:\workspace_QingLiangJiJavaEEQiYeYingYongShiZhang5\staticFactory
└─src\
├─beans.xml
├─lee\
│ └─SpringTest.java
└─org\
└─crazyit\
└─app\
├─factory\
│ └─BeingFactory.java
└─service\
├─Being.java
└─impl\
├─Cat.java
└─Dog.java

下面先定义一个Being接口,静态工厂方法所生产的产品是该接口的实例。下面是接口的两个实现类,静态工厂方法将会产生这两个实现类的实例。

Cat.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package org.crazyit.app.service.impl;
import org.crazyit.app.service.*;

public class Cat implements Being
{
private String msg;
// msg的setter方法
public void setMsg(String msg)
{
this.msg = msg;
}
// 实现接口必须实现的testBeing方法
public void testBeing()
{
System.out.println(msg + ",猫喜欢吃老鼠");
}
}

Dog.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package org.crazyit.app.service.impl;

import org.crazyit.app.service.*;

public class Dog implements Being
{
private String msg;
// msg的setter方法
public void setMsg(String msg)
{
this.msg = msg;
}
// 实现接口必须实现的testBeing()方法
public void testBeing()
{
System.out.println(msg + ",狗爱啃骨头");
}
}

BeingFactory.java

下面的BeingFactory工厂包含了一个getBeing()静态方法,该静态方法用于返回一个Being实例这就是典型的静态工厂类.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package org.crazyit.app.factory;

import org.crazyit.app.service.Being;
import org.crazyit.app.service.impl.Cat;
import org.crazyit.app.service.impl.Dog;

public class BeingFactory
{
// 返回Being实例的静态工厂方法
// arg参数决定返回哪个Being类的实例
public static Being getBeing(String arg)
{
// 调用此静态方法的参数为dog,则返回Dog实例
if (arg.equalsIgnoreCase("dog"))
{
return new Dog();
}
// 否则返回Cat实例
else
{
return new Cat();
}
}
}

上面的BeingFactory类是一个静态工厂类,该类的getBeing()方法是一个静态工厂方法,该方法根据传入的参数决定返回Cat对象,还是Dog对象。
如果需要指定SpringBeingFactory来生产Being对象,则应该按如下静态工厂方法的方式来配置DogBean,CatBean。本应用中的Spring配置文件如下。

beans.xml

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
<?xml version="1.0" encoding="GBK"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 下面配置驱动Spring调用BeingFactory的静态getBeing()方法来创建Bean
该bean元素包含的constructor-arg元素用于为静态工厂方法指定参数,
因此这段配置会驱动Spring以反射方式来执行如下代码:
dog = org.crazyit.app.factory.BeingFactory.getBeing("dog");
-->
<bean id="dog" class="org.crazyit.app.factory.BeingFactory"
factory-method="getBeing">
<!-- 配置静态工厂方法的参数 -->
<constructor-arg value="dog" />
<!-- 驱动Spring以"我是狗"为参数来执行dog的setMsg()方法 -->
<property name="msg" value="我是狗" />
</bean>
<!-- 下面配置会驱动Spring以反射方式来执行如下代码:
cat = org.crazyit.app.factory.BeingFactory.getBeing("cat"); -->
<bean id="cat" class="org.crazyit.app.factory.BeingFactory"
factory-method="getBeing">
<!-- 配置静态工厂方法的参数 -->
<constructor-arg value="cat" />
<!-- 驱动Spring以"我是猫"为参数来执行dog的setMsg()方法 -->
<property name="msg" value="我是猫" />
</bean>
</beans>

从上面的配置文件可以看出,catdog两个Bean配置的cass属性和factory-method属性完全相同——这是因为这两个实例都是由同一个静态工厂类、同一个静态工厂方法生产得到的。配置这两个Bean实例时指定的静态工厂方法的参数值不同,配置工厂方法的参数值使用<contructor-arg>元素,如上配置文件所示。
一旦为<bean>元素指定了factory-method属性, Spring就不再调用构造器来创建Bean实例,而是调用工厂方法来创建Bean实例。如果同时指定了classfactory-method两个属性, Spring就会调用静态工厂方法来创建Bean。上面两段配置驱动Spring执行的Java代码已在注释中给出。
主程序获取Spring容器的catdog两个Bean实例的方法依然无须改变,只需要调用Spring容器的getBean()方法即可。主程序如下。

SpringTest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package lee;

import org.springframework.context.*;
import org.springframework.context.support.*;

import org.crazyit.app.service.*;

public class SpringTest
{
public static void main(String[] args)
{
// 以类加载路径下的beans.xml配置文件创建Spring容器
@SuppressWarnings("resource")
ApplicationContext ctx = new
ClassPathXmlApplicationContext("beans.xml");
Being b1 = ctx.getBean("dog" , Being.class);
b1.testBeing();
Being b2 = ctx.getBean("cat" , Being.class);
b2.testBeing();
}
}

使用静态工厂方法创建实例时必须提供工厂类,工厂类包含产生实例的静态工厂方法。

使用静态工厂方法创建实例时对配置文件的要求

通过静态厂方法创建实例时需要对配置文件进行如下改变:

  1. class属性的值不再是Bean实例的实现类,而是生成Bean实例的静态工厂类
  2. 使用factory-method属性指定创建Bean实例的静态工厂方法
  3. 如果静态工厂方法需要参数,则使用<constructor-arg>元素指定静态工厂方法的参数。

指定Spring使用静态工厂方法来创建Bean实例时, Spring将先解析配置文件,并根据配置文件指定的信息,通过反射调用静态工厂类的静态工厂方法,将该静态工厂方法的返回值作为Bean实例。在这个过程中, Spring不再负责创建Bean实例,Bean实例是由用户提供的静态工厂类负责创建的.

当静态工厂方法创建了Bean实例后, Spring依然可以管理该Bean实例的依赖关系,包括为其注入所需的依赖Bean、管理其生命周期等。