如何在Spring中不再使用Spring创建Bean实例,而是把Bean创建过程转移到开发者手中。
1. 使用静态工厂方法创建Bean
使用静态工厂方法创建Bean实例时,class属性也必须指定,但此时class属性并不是指定Bean实例的实现类,而是静态工厂类。因为Spring需要知道是用哪个工厂来创建Bean实例。另外,还需要使用factory-method来指定静态工厂方法名,Spring将调用静态工厂方法(可能包含一组参数),来返回一个Bean实例,一旦获得了指定Bean实例,Spring后面的处理步骤与采用普通方法创建Bean实例则完全一样。需要注意的是,当使用静态工厂方法来创建Bean时,这个factory-method必须要是静态的。这段阐述听上去有点晕,话不多说,上代码:
先定义一个接口,静态方法产生的将是该接口的实例:
1
2
3
|
public
interface
Animal {
public
void
sayHello();
}
|
下面是接口的两个实现类:
1
2
3
4
5
6
7
8
9
10
11
|
public
class
Cat
implements
Animal {
private
String msg;
//依赖注入时必须的setter方法
public
void
setMsg(String msg){
this
.msg = msg;
}
@Override
public
void
sayHello(){
System.out.println(msg +
",喵~喵~"
);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
|
public
class
Dog
implements
Animal {
private
String msg;
//依赖注入时必须的setter方法
public
void
setMsg(String msg){
this
.msg = msg;
}
@Override
public
void
sayHello(){
System.out.println(msg +
",旺~旺~"
);
}
}
|
下面的AnimalFactory工厂中包含了一个getAnimal的静态方法,该方法将根据传入的参数决定创建哪个对象。这是典型的静态工厂设计模式。
1
2
3
4
5
6
7
8
9
|
public
clas AnimalFactory {
public
static
Animal getAnimal(String type){
if
(
"cat"
.equalsIgnoreCase(type)){
return
new
Cat();
}
else
{
return
new
Dog();
}
}
}
|
如果需要指定Spring使用AnimalFactory来产生Animal对象,则可在Spring配置文件中作如下配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<!-- 配置AnimalFactory的getAnimal方法,使之产生Cat -->
<
bean
id
=
"cat"
class
=
"com.abc.AnimalFactory"
factory-method
=
"getAnimal"
>
<!-- 配置静态工厂方法的参数,getAnimal方法将产生Cat类型的对象 -->
<
constructor-arg
value
=
"cat"
/>
<!-- 通过setter注入的普通属性 -->
<
property
name
=
"msg"
value
=
"猫猫"
/>
</
bean
>
<!-- 配置AnimalFactory的getAnimal方法,使之产生Dog -->
<
bean
id
=
"dog"
class
=
"com.abc.AnimalFactory"
factory-method
=
"getAnimal"
>
<!-- 配置静态工厂方法的参数,getAnimal方法将产生Dog类型的对象 -->
<
constructor-arg
value
=
"dog"
/>
<!-- 通过setter注入的普通属性 -->
<
property
name
=
"msg"
value
=
"狗狗"
/>
</
bean
>
|
从上面的配置可以看出:cat和dog两个Bean配置的class和factory-method完全相同,这是因为两个实例都使用同一个静态工厂类、同一个静态工厂方法产生得到的。只是为这个静态工厂方法指定的参数不同,使用<constructor-arg />元素来为静态工厂方法指定参数。
主程序获取cat和dog两个Bean实例的方法不变,同样只需要调用Spring容器的getBean()即可:
1
2
3
4
5
6
7
8
9
10
|
public
class
Test {
public
static
void
main(String args[]){
ApplicationContext context =
new
ClassPathXmlApplicationContext(
"applicationContext.xml"
);
Animal a1 = context.getBean(
"cat"
, Animal.
class
);
a1.sayHello();
Animal a2 = context.getBean(
"dog"
, Animal.
class
);
a2.sayHello();
}
}
|
输出结果:
1
2
|
猫猫,喵~喵~
狗狗,旺~旺~
|
使用静态工厂方法创建实例时必须提供工厂类和产生实例的静态工厂方法。通过静态工厂方法创建实例时需要对Spring配置文件做如下改变;
-
class属性不在是Bean实例的实现类,而是生成Bean实例的静态工厂类
-
使用factory-method指定生产Bean实例的静态工厂方法
-
如果静态工厂方法需要参数,使用<constructor-arg />元素为其配置
当我们指定Spring使用静态工厂方法来创建Bean实例时,Spring将先解析配置文件,并根据配置文件指定的信息,通过反射调用静态工厂类的静态工厂方法,并将该静态工厂方法的返回值作为Bean实例,在这个过程中,Spring不再负责创建Bean实例,Bean实例是由用户提供的静态工厂方法提供的。
2. 使用实例工厂方法创建Bean
实例工厂方法与静态工厂方法只有一点不同:调用静态工厂方法只需要使用工厂类即可,调用实例工厂方法则必须使用工厂实例。所以在Spring配置上也只有一点区别:配置静态工厂方法指定静态工厂类,配置实例工厂方法则指定工厂实例。同样是上面的例子将AnimalFactory修改为:
1
2
3
4
5
6
7
8
9
|
public
clas AnimalFactory {
public
Animal getAnimal(String type){
//这里仅仅是去掉了static关键字
if
(
"cat"
.equalsIgnoreCase(type)){
return
new
Cat();
}
else
{
return
new
Dog();
}
}
}
|
Spring文件修改为:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<!-- 先配置工厂类 -->
<
bean
id
=
"animalFactory"
class
=
"com.abc.AnimalFactory"
/>
<!-- 这里使用factory-bean指定实例工厂类对象 -->
<
bean
id
=
"cat"
factory-bean
=
"animalFactory"
factory-method
=
"getAnimal"
>
<!-- 同样指定factory-method的参数 -->
<
constructor-arg
value
=
"cat"
/>
<
property
name
=
"msg"
value
=
"猫猫"
/>
</
bean
>
<
bean
id
=
"dog"
factory-bean
=
"animalFactory"
factory-method
=
"getAnimal"
>
<
constructor-arg
value
=
"dog"
/>
<
property
name
=
"msg"
value
=
"狗狗"
/>
</
bean
>
|
测试类不用修改,输出结果和上面相同。