您的位置:首页 > 编程语言 > Java开发

Spring:协调作用域不同步的Bean

2016-06-07 00:10 429 查看
在整理之前,我们首先应该了解Spring支持的五种作用域:

singleton:单例模式,singleton作用域下的Bean将只产生一个实例,因此我们每次获取的实例都是同一实例。

prototype:每次通过getBean()方法获取Bean实例时,都会产生一个新的Bean实例,因此每次的实例都不同

request:用于Web应用,对于一次Http请求,request作用域内的Bean只生成一个Bean,也就是说同一次请求内,每次获取该Bean,

获取的都是同一实例,但是如果到了下次请求(刷新页面)就会再次产生一个Bean实例,但是在每次的请求内多次获取

Bean实例都是同一实例。

session:对于一次http会话,session作用域的Bean将只生成一个实例,仅在Web应用中该作用域才会真的有效。

global session:每个全局的HttpSession对应一个实例

如果不单独在配置文件中设置,默认的作用域为singleton作用域。

如果对Bean的作用域还不是很了解的可以参考此篇博文:http://blog.csdn.net/vipmao/article/details/51565448

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


一:普通方法注入

我们已经知道,singleton是单例模式,该作用域下只产生一个Bean实例,因此我们每次访问所获取的都是同一个实例,但是prototype作用域下的Bean,每次访问都会产生一个新的实例,但是我们试想这么一个情况:当singleton作用域下的Bean依赖一个prototype作用域下的Bean,这就有意思了,在这种情况下,Spring容器会先创建被依赖的prototype作用域的Bean,然后再创建singleton作用域下的主Bean,然后将prototype Bean注入给singleton Bean完成依赖关系,但是singleton
Bean只会创建一次,他的依赖关系也是在创建过程阶段完成的,因此只会完成一次依赖关系的创建,这就导致了每次我们通过singleton Bean访问prototype Bean时,永远访问的只是同一个prototype Bean实例,这样prototype作用域也就丧失了自己原本的作用。

如以下这个例子:


1:chinese Bean实现类

package com.mao.lookup_method;

public  class Chinese implements Person
{
private Dog dog;
public void setDog(Dog dog) {
this.dog = dog;
}
public Dog getDog() {
return dog;
}

@Override
public void hunt()
{
// TODO Auto-generated method stub
System.out.println("我带着"+getDog()+"出去打猎");
System.out.println(getDog().run());
}

}
上面Chinese Bean包含一个hunt()方法,该方法的执行需要依赖Dog的 方法。这里的Dog是一个接口,我们期望每次执行hunt()方法时可以获得不同的gunDog Bean,所以后面我们在配置文件beans.xml将gunDog Bean设置成prototype作用域。


2:gunDog Bean的实现类 该类实现Dog接口

package com.mao.lookup_method;

public class GunDog implements Dog
{
private String name;

public GunDog()
{

}

public void setName(String name)
{
this.name = name;
}

@Override
public String run()
{
return"我是一个叫"+name+"的猎犬,奔跑迅速》》》》" ;

}

}


3:配置文件 beans.xml 配置Bean部分

<bean id="ch" class="com.mao.lookup_method.Chinese">
<!-- Spring只要检测到lookup-method元素,Spring会自动为该元素的name属性数指定的方法提供实现体 -->
<!-- 方法的返回值就是bean属性指定的值 -->
<property name="dog" ref="gunDog"></property>
</bean>
<bean id="gunDog" class="com.mao.lookup_method.GunDog" scope="prototype">
<property name="name" value="旺财"></property>
</bean>


配置文件中我们将gunDog设置成prototype作用股的Bean,并通过普通的依赖注入将prototype Bean注入给singleton Bean

4:测试程序

package com.mao.lookup_method;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test
{
public static void main(String[]args)
{
ApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml");
Chinese ch1=(Chinese) ctx.getBean("ch");
Chinese ch2=(Chinese) ctx.getBean("ch");
System.out.println(ch2==ch2);
ch1.hunt();
ch2.hunt();
}
}


主程序中,我们分两次获取了singleton Bean 并判断是不是同一实例,并分别输出两次的hunt()方法,来看看通过singleton Bean访问prototype Bean是不是也是同一prototype实例

5:输出结果



从结果可以看出,两次获取的singleton Bean是同一实例,但是获取的prototype Bean实例也是同一实例,但是按照常理来讲prototype应该是获取一次创建一个实例啊,这样就是去了prototype的作用。

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


二:lookup方法注入

为了协调作用域不同步的Bean,Spring提供了lookup方法注入,为了使用lookup方法注入,需要如下两步

1:将调用者的Bean实现类定义为抽象类,并定义一个抽象方法来获取被依赖的Bean

2:在<bean>元素中添加<lookup-method>子元素让Spring为调用者Bean的实现类实现指定的抽象方法,

添加<lookup-method>子元素后,<lookup-method>子元素告诉Spring需要实现哪个抽象方法,Spring为抽象方法提供实现体以后,这个方法就会变成具体类,下面Spring就可以创建该Bean的实例了。

使<lookup-method>元素要制定如下两个属性

1:name:告诉Spring需要实现哪个抽象方法

2:bean:指定Spring实现该方法后的返回值


下面是协调作用域不同步Bean的例子,我们将上面的例子稍作修改


1:首先将调用者chinese Bean定义为抽象类,并定义抽象方法获取依赖Bean

package com.mao.lookup_method;

public abstract class Chinese implements Person
{
private Dog dog;
//定义抽象方法,该方法用于获取被依赖的Bean

public abstract Dog getDog();

@Override
public void hunt()
{
// TODO Auto-generated method stub
System.out.println("我带着"+getDog()+"出去打猎");
System.out.println(getDog().run());
}

}
上面我们将调用者Chinese Bean定义为抽象类,并定义抽象方法getDog()用于获取被依赖Bean ,该方法返回的是被依赖Bean的实例,这里返回的就是gunDog Bean实例,该方法由Spring容器自动调用,然后我们再多次调用hunt()方法从singleton Bean获取prototype Bean,看看是不是获取的还是同一prototype Bean实例。


2:被依赖的gunDog Bean

package com.mao.lookup_method;

public class GunDog implements Dog
{
private String name;

public GunDog()
{

}

public void setName(String name)
{
this.name = name;
}

@Override
public String run()
{
return"我是一个叫"+name+"的猎犬,奔跑迅速》》》》" ;

}

}


3:配置文件 beans.aml

<bean id="ch" class="com.mao.lookup_method.Chinese">
<!-- Spring只要检测到lookup-method元素,Spring会自动为该元素的name属性数指定的方法提供实现体 -->
<!-- 方法的返回值就是bean属性指定的值 -->
<lookup-method name="getDog" bean="gunDog"/>
</bean>
<bean id="gunDog" class="com.mao.lookup_method.GunDog" scope="prototype">
<property name="name" value="旺财"></property>
</bean>


上面在定义Chinese Bean的时候,添加了<lookup-method>子元素,该元素内有name、bean属性,就是说Spring应负责实现getDog()方法,该方法的返回值是容器中的gunDog Bean实例。并且我们将gunDog Bean作用域设置成prototype


4:测试函数 Test.java

package com.mao.lookup_method;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test
{
public static void main(String[]args)
{
ApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml");
Chinese ch1=(Chinese) ctx.getBean("ch");
Chinese ch2=(Chinese) ctx.getBean("ch");
System.out.println(ch2==ch2);
ch1.hunt();
ch2.hunt();
}
}


主函数分两次获取了singleton Bean,并通过hun()t两次访问prototype Bean


5:运行结果



可以看出,两次访问的prototype是两个不同的实例。

使用lookup方法注入后,系统每次调用getDog()方法都会获得新的gunDog 实例,这就保证当singleton作用域Bean需要prototype Bean时,直接调用GetDog方法即可获得全新实例。这也正是lookup方法注入和普通方法注入的区别
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息