在整理之前,我们首先应该了解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方法注入和普通方法注入的区别