Spring基本用法4——创建Bean的三种方式 博客分类: Spring Spring创建Bean的三种方式静态工厂方法实例工厂方法
前言:在大多数情况下,Spring容器直接通过new关键字调用构造器来创建Bean实例,而class属性指定了Bean实例的实现类。因此,<bean../>元素必须指定Bean实例的class属性,但这并不是实例化Bean的唯一方法,本文介绍三种实例化Bean的方法。
本篇文章重点关注以下问题:
- 调用构造器创建Bean;
- 调用静态工厂方法创建Bean;
- 调用实例工厂方法创建Bean。
1. 使用构造器创建Bean
使用构造器创建Bean有两种可能情况,一是不采用构造注入,Spring底层调用Bean类的无参数构造器来创建实例,二是采用构造注入时,Spring容器使用带对应参数的构造器来创建Bean。
1.1 不采用构造注入
不采用构造注入时,Spring底层会调用Bean类的无参数构造器来创建实例,因此要求该Bean类提供无参数的构造器。在这种情况下class元素是必须的(除非采用继承),class属性的值就是Bean实例的实现类。
当Spring使用默认构造器来创建Bean实例时,Spring对Bean实例的所有属性执行默认初始化,即所有基本类型的值初始化为0或false;所有引用类型的值初始化为null。同时,Spring会根据配置文件决定的依赖关系,先实例化被依赖的Bean实例,然后为Bean注入依赖关系,最后将一个完整的Bean实例返回给程序。
1.2 采用构造注入
如果采用构造注入,则要求配置文件为<bean../>元素添加<constructor-arg../>子元素,每个<constructor-arg../>子元素配置一个构造器参数。Spring容器将使用带对应参数的构造器来创建Bean实例,Spring调用构造器传入的参数即可用于初始化Bean的实例,最后也将一个完整的Bean实例返回给程序。
注:对于 使用构造器创建Bean的实例可见http://super-wangj.iteye.com/admin/blogs/2383803一文,在此不过多阐述。
2. 使用静态工厂方法创建Bean
使用静态工厂方法创建Bean实例时,需指定两个属性:
- class:该属性的值为静态工厂类的类名。(Spring通过该属性知道由哪个工厂类来创建Bean)
- factory-method:该属性指定静态工厂方法来生产Bean实例。(工厂方法必须是静态的)
2.1 定义Bean:一个接口及其两个实现
首先是定义Person接口:
public interface Person { public void testBeing(); }
然后是其两个实现类Chinese.java和American.java
Chinese.java:
public class Chinese implements Person { private String name; // name的setter方法 public void setName(String name) { this.name = name; } // 实现接口必须实现的testBeing方法 public void testBeing() { System.out.println("我是中国人,名字是:" + name); } }
American.java:
public class American implements Person { private String name; // name的setter方法 public void setName(String name) { this.name = name; } @Override public void testBeing() { System.out.println("我是美国人,名字是:" + name); } }
2.2 编写静态工厂类
下面的PersonFactory静态工厂包含了一个getBeing(byte mode)静态工厂方法,该方法用于返回一个Person实例,具体是Chinese对象还是American对象需根据传入的参数决定。
public class PersonFactory { public static final byte COUNTRY_CHINA = 0; // 代表中国人 public static final byte COUNTRY_AMERI = 1; // 代表美国人 /** * 返回Being实例的静态工厂方法 * arg参数决定返回哪个Person类的实例 * @param mode * @return */ public static Person getBeing(byte mode) { // 根据传入的参数,确定返回哪个实例 if (mode == COUNTRY_CHINA) { return new Chinese(); } if (mode == COUNTRY_AMERI) { return new American(); } throw new RuntimeException("配置有误"); } }
2.3 按静态工厂方法的方式配置Chinese Bean和American Bean
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd"> <!-- 下面配置驱动Spring调用PersonFactory的静态getBeing()方法来创建Bean 该bean元素包含的constructor-arg元素用于为静态工厂方法指定参数, --> <bean id="chinese" class="com.wj.chapter4.staticFactory.PersonFactory" factory-method="getBeing"> <!-- 配置静态工厂方法的参数,0代表中国人 --> <constructor-arg value="0"/> <!-- 驱动Spring以"熊燕子"为参数来执行chinese的setName()方法 --> <property name="name" value="熊燕子"/> </bean> <bean id="american" class="com.wj.chapter4.staticFactory.PersonFactory" factory-method="getBeing"> <!-- 配置静态工厂方法的参数,1代表美国人 --> <constructor-arg value="1"/> <!-- 驱动Spring以"wj"为参数来执行american的setName()方法 --> <property name="name" value="wj"/> </bean> </beans>
由上述配置文件可看出,chinese和american两个Bean配置的class属性和factory-method属性完全相同——这是因为这两个实例都是由同一个静态工厂类、同一个静态工厂方法生产得到的。配置这两个Bean实例时指定的静态工厂方法的参数值不同(一个是0,一个是1),并以此为依据生产不同的对象。
一旦为<bean../>元素指定了factory-method属性,Spring就不再调用构造器来创建Bean实例,而是调用静态工厂方法来创建Bean实例。如果同时指定了class和factory-method属性,Sring会调用静态工厂方法来创建Bean。
2.4 编写测试代码,查看测试结果
public class TestMain { // 1.指明xml配置文件位置,便于Spring读取,从而知道Bean的相关信息 private static final String PATH_XML = "com/wj/chapter4/staticFactory/applicationContext-createBean.xml"; @SuppressWarnings("resource") public static void main(String[] args) { // 2.以类加载路径下的配置文件创建ClassPathResource实例 ApplicationContext ctx = new ClassPathXmlApplicationContext(PATH_XML); // 3.测试获取Chinese对象 Person chinese = ctx.getBean("chinese" , Person.class); chinese.testBeing(); // 4.测试获取American对象 Person american = ctx.getBean("american" , Person.class); american.testBeing(); } }
运行结果为:
2.5 小结
使用静态工厂方法创建实例时必须提供工厂类,工厂类包含产生实例的静态工厂方法。通过静态工厂方法创建实例时需要对配置文件进行如下改变:
- class属性的值不再是Bean实例的实现类,二是生成Bean实例的静态工厂方法;
- 使用factory-method属性指定创建Bean实例的静态工厂方法;
- 如果静态工厂方法需要参数,则使用<constructor-arg..>元素指定静态工厂方法的参数。
注:通过静态工厂方法创建了Bean实例后,Spring依然可以管理该Bean实例的依赖关系,包括为其注入所需的依赖Bean、管理其生命周期等。
3. 使用实例工厂方法创建Bean
实例工厂方法与静态工厂方法只有一点不同:调用静态工厂方法只需要使用工厂类即可,而调用实例工厂方法则需要工厂实例。所以,配置实例工厂方法与配置静态工厂方法基本类似,只有一点区别:配置静态工厂方法使用class指定静态工厂类,而配置实例工厂方法则使用factory-bean指定工厂实例。
使用实例工厂方法时,配置Bean实例的<bean../>元素无需class属性,因为Spring容器不再直接实例化该Bean,Spring容器仅仅调用实例工厂的工厂方法,由工厂方法负责创建Bean实例。
采用实例工厂方法创建Bean的<bean../>元素时,需要指定如下两个属性:
- factory-bean:该属性的值为工厂Bean的id;
- factory-method:该属性指定实例工厂的工厂方法。
注:与静态方法类似,如果需要在调用实例工厂方法时传入参数,则使用<constructor-arg../>元素确定参数值。
3.1 定义Bean:一个接口及其两个实现
首先是Person接口:
public interface Person { // 定义一个打招呼的方法 public void sayHello(); }
然后是Person接口的两个实现类:Chinese.java和American.java
Chinese.java:
public class Chinese implements Person { private String name; // name的setter方法 public void setName(String name) { this.name = name; } // 实现Person接口方法 @Override public void sayHello() { System.out.println("你好,我是中国人,名字是:" + name); } }
American.java:
public class American implements Person { private String name; // name的setter方法 public void setName(String name) { this.name = name; } // 实现Person接口方法 @Override public void sayHello() { System.out.println("你好,我是美国人,名字是:" + name); } }
3.2 编写实例工厂
PersonFactory是负责产生Person对象的实例工厂,该工厂类里提供了一个getPerson()方法,该方法根据传入的mode参数决定产生哪种Person对象。
public class PersonFactory { public static final byte COUNTRY_CHINA = 0; // 代表中国人 public static final byte COUNTRY_AMERI = 1; // 代表美国人 /** * 实例工厂方法(非静态) * mode参数决定返回哪个Person的实例 * @param mode * @return */ public Person getPerson(byte mode) { // 根据传入的参数,确定返回哪个实例 if (mode == COUNTRY_CHINA) { return new Chinese(); } if (mode == COUNTRY_AMERI) { return new American(); } throw new RuntimeException("配置有误"); } }
3.3 按实例工厂方法的方式配置Chinese Bean和American Bean
配置实例工厂创建Bean与配置静态工厂创建Bean基本类似,只需将原来的静态工厂类改为现在的工厂实例即可。
!-- 配置工厂Bean,该Bean负责产生其他Bean实例 --> <bean id="personFactory" class="com.wj.chapter4.instanceFactory.PersonFactory"/> <!-- 下面配置驱动Spring调用personFactory Bean的getPerson()方法来创建Bean 该bean元素包含的constructor-arg元素用于为工厂方法指定参数. --> <bean id="chinese" factory-bean="personFactory" factory-method="getPerson"> <!-- 配置实例工厂方法的参数,0代表中国人. --> <constructor-arg value="0"/> <!-- 驱动Spring以"熊燕子"为参数来执行chinese的setName()方法 --> <property name="name" value="熊燕子"/> </bean> <bean id="american" factory-bean="personFactory" factory-method="getPerson"> <!-- 配置实例工厂方法的参数,1代表美国人. --> <constructor-arg value="1"/> <!-- 驱动Spring以"wj"为参数来执行american的setName()方法 --> <property name="name" value="wj"/> </bean>
3.4 编写测试代码,查看测试结果
public class SpringTest { // 1.指明xml配置文件位置,便于Spring读取,从而知道Bean的相关信息 private static final String PATH_XML = "com/wj/chapter4/instanceFactory/applicationContext-createBean.xml"; @SuppressWarnings("resource") public static void main(String[] args) { // 2.以类加载路径下的配置文件创建ClassPathResource实例 ApplicationContext ctx = new ClassPathXmlApplicationContext(PATH_XML); // 3.测试获取Chinese对象 Person p1 = ctx.getBean("chinese", Person.class); p1.sayHello(); // 4.测试获取American对象 Person p2 = ctx.getBean("american", Person.class); p2.sayHello(); } }
测试结果如下:
3.5 小结
可以发现,调用实例工厂方法创建Bean,与调用静态工厂创建Bean的用法基本类似。区别如下:
- 配置实例工厂方法创建Bean,必须使用将实例工厂配置成Bean实例;而配置静态工厂方法创建Bean,则无需配置工厂Bean。
- 配置实例工厂方法创建Bean,必须使用factory-bean属性确定工厂Bean;而配置静态工厂方法创建Bean,则使用class元素确定静态工厂类。
- 都需要使用factory-method属性指定产生Bean实例的工厂方法;
- 工厂方法如果需要参数,都使用<constructor-arg../>元素指定参数值;
- 普通的设置注入,都使用<property../>元素确定参数值。
代码下载地址:http://pan.baidu.com/s/1jIKH9Y2,密码:6jd9