有一个学生,他不同的时间段可能呆在不同的地方。
有一个Student类:
public class Student {
private int age;
private String name;
private Address address;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Address getAddress() {
//return address;
return (Address)beanFactory.getBean("address");
}
public void setAddress(Address address) {
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/*他不同的时间段可能呆在不同的地方。我们希望每次调用whereAmI方法的时候,得到的是一个新的Address对象
*/
public void whereIAm(){
System.out.println("我现在在: "+getAddress());
}
}
当然,还有一个表示地点的Address类:
public class Address {
Random rand = new Random(); //在创建这个对象的时候,地址实际已经确定下来了,往下看
int r;
private String[] adds = {
"宿舍",
"教室",
"食堂",
"操场",
"网吧",
"家里",
"公交车上"
};
public Address(){
r = rand.nextInt(7);
}
@Override
public String toString() {
// TODO Auto-generated method stub
return adds[r]; //返回模拟的地点
}
}
beans.xml的部分配置:
注意:scope="prototype"
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
<bean id = "address" class="com.edgar.test.Address" scope="prototype">
</bean>
<bean id = "stu1" class="com.edgar.test.Student">
<property name="address">
<ref bean="address"/>
</property>
</bean>
</beans>
测试类:
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
BeanFactory factory = new XmlBeanFactory(
new ClassPathResource("beans.xml"));
Student stu = (Student)factory.getBean("stu1");
stu.whereIAm();
stu.whereIAm();
stu.whereIAm();
}
}
输出结果:
我现在在: 操场
我现在在: 操场
我现在在: 操场
显然,这种最一般的配置不能达到我们的要求,因为whereIAm方法每次得到的都是同一个Address对象,三次打印的结果也必然相同。
我们可以通过Spring提供的以下几种方式达到我们的目的.
1 方法注入
如果我们想在某一个方法被调用的时候,重新获得某个对象,我们要保证这个方法符合下面的格式:
<public|protected> [abstract] <return-type> theMethodName(no-arguments)
也就是说,该方法必须能被子类重写,因为容器会为我们进行方法注入的对象使用CGlib动态生成一个子类实现,从而替代当前对象,既然我们的getAddress()方法已经满足以上方法的声明格式,剩下唯一要做的就是配置该类,如下所示:
<bean id = "stu1" class="com.edgar.test.Student">
<lookup-method name="getAddress" bean="address"/>
</bean>
通过<lookup-method>的name属性指定需要注入的方法名,bean属性指定需要注入的对象,当getAddress方法被调用的时候,容器可以每次返回一个新的Address类型的实例,所以,这个我们再检查输出的结果,输出的实例引用应该是不同的:
我现在在: 公交车上
我现在在: 教室
我现在在: 网吧
2 是同BeanFactoryAware接口
我们知道,即使没有方法注入,只要是在实现GetAddress()方法的时候,能够保证每次调用BeanFactory的getBean("address"),就同样可以每次取的新的Address对象的实例,现在我们唯一需要的,就是让Student拥有一个BeanFactory的引用。
Spring框架提供了一个BeanFactoryAware接口,容器BeanFactory的引用。在实例化实现了该接口的Bean定义的过程中,会自动将容器本身注入该Bean,这样,该Bean就拥有了BeanFactory的引用。
BeanFactory的定义如下所示:
public interface BeanFactoryAware{
void setBeanFactory(BeanFactory beanFactory) throws BeansException {
// TODO Auto-generated method stub
this.beanFactory = beanFactory;
}
}
所以,修改之后的Student类是这样:
public class Student implements BeanFactoryAware{
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
// TODO Auto-generated method stub
this.beanFactory = beanFactory;
}
private int age;
private String name;
private Address address;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Address getAddress() {
//System.out.println(address.getClass().getName());
//return address;
return (Address)beanFactory.getBean("address");
}
public void setAddress(Address address) {
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void whereIAm(){
System.out.println("我现在在: "+getAddress());
}
}
beans.xml简化为:
<bean id = "address" class="com.edgar.test.Address" scope="prototype">
</bean>
<bean id = "stu1" class="com.edgar.test.Student">
</bean>
2 使用ObjectFactoryCreatingFactoryBean
ObjectFactoryCreatingFactoryBean是Spring提供的一个FactoryBean实现,它返回一个ObjectFactory实例,从ObjectFactoryCreatingFactoryBean返回的这个ObjectFactory实例可以为我们返回容器管理的相关对象,实际上,ObjectFactoryCreatingFactoryBean实现了BeanFactoryAware接口,它返回的ObjectFactory实例只是特定于与Spring容器进行交互的一个实现而已。使用它的好处就是,隔离了客户端对象对BeanFactory的直接引用。
现在的Student类要改成这样:
public class Student {
private ObjectFactory addressFactory;
private int age;
private String name;
private Address address;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Address getAddress() {
return (Address)addressFactory.getObject();
}
public void setAddress(Address address) {
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void whereIAm(){
System.out.println("我现在在: "+getAddress());
}
public void setAddressFactory(ObjectFactory addressFactory) {
this.addressFactory = addressFactory;
}
}
然后,向Student中注入ObjectFactory 即可!
<bean id="stu1" class="com.edgar.test.Student">
</bean>
<bean id="addressFactory"
class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
<property name="targetBeanName">
<idref bean="address" />
</property>
</bean>
<bean id="stu1" class="com.edgar.test.Student">
<property name="addressFactory">
<idref bean="addressFactory" />
</property>
</bean>
4 方法替换
这种方式,可能用的很少很少,我就不说了,大家可以看一下Spring的org.springframework.beans.factory.support.MethodReplacer借口,我们需要自己写一个这个借口的实现类,实现MethodReplacer接口中的reimplement方法,这个方法里面的执行内容会替换你在配置文件中指定的方法。
在配置文件中这样配置:
<bean id="stu1" class="com.edgar.test.Student">
<property name="addressFactory">
<idref bean="addressFactory" />
</property>
<replaced-method name="getAddress" replacer="addressReplacer"/>
</bean>
<bean id ="addressReplacer" class="你的实现类"></bean>
这样,你重写的方法逻辑就会替换原来getAddress方法的逻辑。
Spring的功能很强大~~~