Bean实例的创建方式及依赖配置

大多数情况下,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 "钢斧砍柴真快";
	}
	
}



Spring配置文件:

<?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..../>元素确定参数值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值