大多数情况下,BeanFactory直接通过new 关键字调用构造器来创建Bean实例,而class属性值定了Bean实例的实现类。因此,<bean.../>元素必须制定Bean实例的class属性,但这不是实例化Bean的唯一方法。
创建Bean通常有如下方法:
1)调用构造器创建Bean实例。
2)调用静态工程方法创建Bean
3)调用实例工厂方法创建Bean。
第一种情况:使用构造器创建Bean实例
使用构造器创建Bean实例是最常见的情况,如果采用设置注入的方式,要求该类提供无参数的构造器。这种情况下,class元素是必须的,class属性的值就是Bean实例的实现类。
BeanFactory将使用默认构造器来创建Bean实例,该实例是一个默认实例,Spring对Bean实例的所有属性执行默认初始化,即所有基本类型的值初始化为0或FALSE;所有引用类型的值初始化为null。
接下来,BeanFactory会根据配置文件决定依赖关系,先实例化被依赖的Bean实例,然后为Bean注入依赖关系,最后将一个完成的Bean实例返回给程序,该Bean实例的所有属性,已经由Spring容器完成了初始化。
调用Bean的接口和实现类:
package org.crazyit.app.service;
public interface Person
{
//定义一个使用斧子的方法
public void useAxe();
}
package org.crazyit.app.service.impl;
import org.crazyit.app.service.*;
public class Chinese
implements Person
{
private Axe axe;
//设置注入所需要的Setter方法
public void setAxe(Axe axe) {
this.axe = axe;
}
//默认的构造器
public Chinese()
{
System.out.println("Spring实例化主调Bean:Chinese实例。。。");
}
//实现Person接口的useAxe方法
public void useAxe()
{
//调用axe的chop()方法
//表明Person对象依赖于axe对象
System.out.println(axe.chop());
}
}
下面给出的是Person接口依赖Bean的接口和实现类。
package org.crazyit.app.service;
public interface Axe
{
//Axe接口里有个砍的方法
public String chop();
}
package org.crazyit.app.service.impl;
import org.crazyit.app.service.*;
public class SteelAxe
implements Axe
{
//默认的构造器
public SteelAxe(){
System.out.println("Spring实例化依赖Bean:SteelAxe实例。。。.");
}
//实现Axe接口的chop方法
public String chop()
{
return "钢斧砍柴真快";
}
}
<?xml version="1.0" encoding="GBK"?>
<!-- Spring配置文件的根元素,使用spring-beans-3.0.xsd语义约束 -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- 配置chinese实例,其实现类是Chinese -->
<bean id="chinese" class="org.crazyit.app.service.impl.Chinese">
<!-- 使用property元素用来指定需要容器注入的属性,axe属性需要容器注入 -->
<property name="axe" ref="steelAxe"></property>
</bean>
<!-- 配置stoneAxe实例,其实现类是StoneAxe -->
<bean id="stoneAxe" class="org.crazyit.app.service.impl.StoneAxe"/>
<!-- 配置steelAxe实例,其实现类是SteelAxe -->
<bean id="steelAxe" class="org.crazyit.app.service.impl.SteelAxe"/>
<context:annotation-config/>
</beans>
测试程序:
package lee;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.crazyit.app.service.*;
import org.junit.Test;
public class BeanTest
{
@Test
public void testBean() throws Exception {
//创建Spring容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");
//获取chinese 实例
Person p = ctx.getBean("chinese" , Person.class);
//调用useAxe()方法
p.useAxe();
}
}
上面的程序获得一个Person对象, Spring会先创建Person所以来的Bean实例,在创建一个默认的Person实例,燃火将Person所以来的Bean实例注入Person实例。结果如下:
第二种方法:使用静态工厂方法
使用静态工厂方法创建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.../>元素传入。
下面先定义一个接口,静态工厂方法将会产生两个实现类的实例。
package org.crazyit.app.service;
public interface Being
{
//接口定义testBeing方法
public void testBeing();
}
package org.crazyit.app.service.impl;
import org.crazyit.app.service.*;
public class Cat
implements Being
{
private String msg;
//依赖注入时候必须的setter方法
public void setMsg(String msg)
{
this.msg = msg;
}
//实现接口必须实现的testBeing方法
public void testBeing()
{
System.out.println(msg +
" 猫喜欢吃老鼠");
}
}
package org.crazyit.app.service.impl;
import org.crazyit.app.service.*;
public class Dog
implements Being
{
private String msg;
//依赖注入时候必须的setter方法
public void setMsg(String msg)
{
this.msg = msg;
}
//实现接口必须实现的testBeing方法
public void testBeing()
{
System.out.println(msg +
" 狗爱啃骨头");
}
}
下面的BeingFactory工厂包含了一个getBeing静态方法,该静态方法用于返回一个Being实例,这是典型的静态工厂的设计模式。
package org.crazyit.app.factory;
import org.crazyit.app.service.impl.*;
import org.crazyit.app.service.*;
public class BeingFactory
{
/**
* 获取Being实例的静态工厂方法
* @param 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对象。
如果需要指定Spring让BeingFactory来生产Being对象,则应该按如下静态工厂方法的方式来配置Dog、Cat Bean。
<?xml version="1.0" encoding="GBK"?>
<!-- Spring配置文件的根元素,使用spring-beans-3.0.xsd语义约束 -->
<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-3.0.xsd">
<!-- 配置BeingFactory的getBeing方法产生dog Bean -->
<bean id="dog" class="org.crazyit.app.factory.BeingFactory"
factory-method="getBeing">
<!-- 配置静态工厂方法的参数 -->
<constructor-arg value="dog"/>
<!-- property配置普通依赖注入属性 -->
<property name="msg" value="我是狗"/>
</bean>
<!-- 配置BeingFactory的getBeing方法产生dog Bean -->
<bean id="cat" class="org.crazyit.app.factory.BeingFactory"
factory-method="getBeing">
<!-- 配置静态工厂方法的参数 -->
<constructor-arg value="cat"/>
<!-- property配置普通依赖注入属性 -->
<property name="msg" value="我是猫"/>
</bean>
</beans>
主程序获取Spring容器的cat、dog两个Bean实例的方法依然无须改变,只需要调用Spring容器的getBean()方法即可。
package lee;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.crazyit.app.service.*;
import org.junit.Test;
public class BeanTest
{
@Test
public void testBean() throws Exception {
//以类加载路径下的配置文件创建ClassPathResource实例
ApplicationContext ctx = new
ClassPathXmlApplicationContext("bean.xml");
Being b1 = ctx.getBean("dog" , Being.class);
b1.testBeing();
Being b2 = ctx.getBean("cat" , Being.class);
b2.testBeing();
}
}
使用静态工厂方法创建实例时必须提供工厂类,工厂类包含产生实例的静态工厂方法。通过静态工厂方法创建实例需要对配置文件进行如下改变:
class属性的值不在是Bean实例的实现类,而是生成Bean实例的静态工厂类。
使用factory-method属性指定生产Bean实例的静态工厂方法
如果静态工厂需要参数,则使用<constructor-arg.../>元素指定静态工厂方法的参数。
当我们指定Spring使用静态工厂方法创建Bean实例时,Spring将先解析配置文件,并根据配置文件指定的信息,通过反射调用静态工程类的静态工厂方法,将静态工厂方法的返回值作为Bean实例。在这个过程中,Spring不在负责创建Bean实例,Bean实例是由用户提供的静态工厂类负责创建的。
当静态工厂方法创建了Bean实例后,Spring依然可以管理Bean实例的依赖关闭,包括为其注入所需要的依赖关系,管理生命周期等。
第三种方法:调用实例工厂方法创建Bean
实例工厂方法与静态工厂方法只有一点不同:调用静态工厂方法只需要使用工厂类即可,调用实例工厂方法则需要使用工厂实例。所以配置实例工厂方法与配置静态工厂方法基本相似,只有一点区别:配置静态工厂方法指定静态工厂类,配置实例工厂方法则指定工厂实例。
相关代码文件如下:
package org.crazyit.app.service;
public interface Person
{
//定义一个打招呼的方法
public String sayHello(String name);
//定义一个告别的方法
public String sayGoodBye(String name);
}
package org.crazyit.app.service.impl;
import org.crazyit.app.service.*;
public class Chinese
implements Person
{
//实现Person接口必须实现如下两个方法
public String sayHello(String name)
{
return name + ",您好";
}
public String sayGoodBye(String name)
{
return name + ",下次再见";
}
}
package org.crazyit.app.service.impl;
import org.crazyit.app.service.*;
public class American
implements Person
{
//实现Person接口必须实现如下两个方法
public String sayHello(String name)
{
return name + ",Hello!";
}
public String sayGoodBye(String name)
{
return name + ",Good Bye!";
}
}
package org.crazyit.app.factory;
import org.crazyit.app.service.*;
import org.crazyit.app.service.impl.*;
public class PersonFactory
{
/**
* 获得Person实例的实例工厂方法
* @param ethnic 决定返回哪个Person实例的参数
* @return 返回Person实例
*/
public Person getPerson(String ethnic)
{
if (ethnic.equalsIgnoreCase("chin"))
{
return new Chinese();
}
else
{
return new American();
}
}
}
<?xml version="1.0" encoding="GBK"?>
<!-- Spring配置文件的根元素,使用spring-beans-3.0.xsd语义约束 -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- 配置工厂Bean,该Bean负责产生其他Bean实例 -->
<bean id="personFactory"
class="org.crazyit.app.factory.PersonFactory"/>
<!-- 采用实例工厂创建Bean实例,factory-bean指定工厂Bean的id属性
factory-method属性指定工厂Bean的实例工厂方法 -->
<bean id="chinese" factory-bean="personFactory"
factory-method="getPerson">
<!-- 调用工厂方法时,传入的参数通过constructor-arg元素指定 -->
<constructor-arg value="chin"/>
</bean>
<bean id="american" factory-bean="personFactory"
factory-method="getPerson">
<constructor-arg value="ame"/>
</bean>
<context:annotation-config/>
</beans>
测试程序:
package lee;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.crazyit.app.service.*;
import org.junit.Test;
public class BeanTest
{
@Test
public void testBean() throws Exception {
//以类加载路径下的配置文件创建ClassPathResource实例
ApplicationContext ctx = new
ClassPathXmlApplicationContext("bean.xml");
Person p1 = ctx.getBean("chinese"
, Person.class);
System.out.println(p1.sayHello("Mary"));
System.out.println(p1.sayGoodBye("Mary"));
Person p2 = ctx.getBean("american"
, Person.class);
System.out.println(p2.sayHello("Jack"));
System.out.println(p2.sayGoodBye("Jack"));
}
}
测试结果:
调用实例工厂方法创建Bean和调用静态工厂方法创建Bean的用法基本相似。区别如下:
1)调用实例工厂方法创建Bean,必须将实例工厂配置成Bean实例。而静态工厂方法创建Bean,则无需配置工厂Bean
2)调用实例工厂方法创建Bean,必须使用factory-bean属性确定工厂Bean。确定工厂的Bean。而静态工厂方法创建Bean,则使用class元素确定静态工厂类。
3)都需要使用factory-method属性指定产生Bean实例的工厂方法
4)工厂方法如果需要参数,都使用<constructor-arg.../>元素指定参数值。
5)其它依赖注入属性,都是用<proterty..../>元素确定参数值。