Spring Prototype假象

有一个学生,他不同的时间段可能呆在不同的地方。

有一个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的功能很强大~~~

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值