Spring FrameWork从入门到NB -Bean Scopes

Bean Scopes,Spring Bean的作用域,也就是Spring创建的Bean实例的生效范围。

Spring提供了如下6中不同的作用域:

  1. singleton: 单例Bean,每一个Spring IoC容器中只有一个Bean实例,这是Bean的默认作用域。
  2. prototype:原型Bean,每请求一次就生成一个新的Bean实例
  3. request:仅对 web-aware Spring ApplicationContext生效,每一次HTTP请求生成一个Bean实例。
  4. session: 仅对 web-aware Spring ApplicationContext生效,每一个HTTP session对应一个Bean 实例。
  5. application: 仅对 web-aware Spring ApplicationContext生效,ServletContext生命周期对应一个Bean实例。
  6. websocket:仅对 web-aware Spring ApplicationContext生效,websocket请求对应一个Bean实例。

其中有4个是和web-aware Spring ApplicationContext相关的,也就是和web应用相关,最常用的是singleton和prototype两种。

单例Bean

singleton,单例Bean,是Spring默认的Bean作用域,我们在配置Bean的时候如果没有指定作用域,那么他的作用域就是singleton。

Spring官网的一张图一目了然的讲清楚了单例Bean的含义:

Spring FrameWork从入门到NB -Bean Scopes_第1张图片

定义了一个id为accountDao的Bean,被其他3个Bean引用。如图,我们在定义accountDao的时候没有指定作用域,默认的,他的作用域就是singleton,单例Bean。那么,在整个Spring IoC容器中就只创建一个accountDao的Bean实例,3个引用类引用的其实是同一个accountDao实例。

这种情况系按我们一定要注意accountDao的线程安全问题!使用单例Bean的时候你一定要注意线程安全问题,尽可能的避免在单例Bean中使用属性对象,或者你非常明确的知道该属性不需要关注线程安全性!

接下来思考一个问题:定义为单例Bena就一定表示整个Spring IoC容器中只有该类的一个对象吗?

答案显示是No,你当然可以在Spring IoC容器中为某一个类创建多个单例对象。

原型Bean

原型Bean指的是Spring IoC容器中会有多个Bena实例,应用没请求一次,Spring IoC容器就会生成一个新的Bean实例返回。
Spring FrameWork从入门到NB -Bean Scopes_第2张图片
如图,accountDao定义为原型Bean,被其他3个Bean引用,不管其他3个Bean是单例还是原型Bean,Spring IoC注入的accountDao都会是3个不同的对象。

原型Bean在声明的时候需要显式指定scope:

 
    

也通过注解方式指定scope:

@Component
@Scope("prototype")
public class DependencyB implements IDependencyB{
    @Autowired
    //@Qualifier(value="dependencyA3")
    private IDependencyA dependencyA;
    public static void main(String[] args) {
        System.out.println("I am a new comer...hello world ");
    }
    @Override
    public void testB(){
        System.out.print("This is DependencyB's test and ...");
        dependencyA.test();
    }

启动类加测试代码:

public class App {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfiguration.class);
        System.out.println("Now start to getbean...");
        DependencyB dependencyB = applicationContext.getBean(DependencyB.class);
        dependencyB.testB();
        System.out.println(dependencyB);
        dependencyB=applicationContext.getBean(DependencyB.class);
        System.out.println(dependencyB);
    }
}

执行启动类:

Now start to getbean...
This is DependencyB's test and ...I am DependencyA test...
springTest.DependencyB@53ca01a2
springTest.DependencyB@358c99f5

可以看到两次获取DependencyB,得到的是不同的对象。

原型Bean的特殊之处还在于:创建的时候交给了Spring IoC,但是Spring IoC并不负责销毁。但是好消息是,由于原型Bean的特殊性,所以,Spring IoC容器也没有必要在每次创建之后缓存该实例,Spring IoC容器只需要new出一个对象(其实不是New的,是通过反射机制或者CGLIB创建的)直接返回给应用就OK,所以,也就不会导致内存泄漏,

单例Bena和原型Bean的依赖

如果一个原型Bean要依赖一个单例Bena,其实是没有任何问题的,因为依赖注入是发生在Bean实例化之后的属性赋值阶段,原型Bean在每次请求的时候都会进行一次属性赋值、都会执行一次依赖注入,单例Bean depencyA会注入到原型Bean dependencyB中,不会存在任何问题。

其实我们上面的例子,原型Bean dependencyB就是依赖单例Bean dependencyA的,我们把DependencyB的test方法稍加改造、打印一下依赖对象dependencyA:

    @Override
    public void testB(){
        System.out.print("This is DependencyB's test and ..."+dependencyA);
        dependencyA.test();
    }

执行启动类:

Now start to getbean...
This is DependencyB's test and ...springTest.DependencyA@53ca01a2I am DependencyA test...
springTest.DependencyB@358c99f5
This is DependencyB's test and ...springTest.DependencyA@53ca01a2I am DependencyA test...
springTest.DependencyB@3ee0fea4

我们会发现两次获取到的虽然是不同的dependencyB对象,但是他们的依赖对象dependencyA却是相同的对象,因为dependencyA是单例Bean。

但是反过来就不是这么回事了:如果一个单例Bean依赖一个原型Bean会怎么样?

为此,我们把上例中的DependencyB修改为单例Bean,把DependencyA修改为原型Bean,做一下测试。

只需要把@Scope("prototype")注解从DependencyB类转移到DependencyA上即可。

运行启动类:

 Now start to getbean...
This is DependencyB's test and ...springTest.DependencyA@53ca01a2I am DependencyA test...
springTest.DependencyB@358c99f5
This is DependencyB's test and ...springTest.DependencyA@53ca01a2I am DependencyA test...
springTest.DependencyB@358c99f5

我们发现虽然定义DependencyA为原型Bean,但是两次获取到的DependencyB的依赖对象DependencyA是同一个对象。

这也不难理解,Bena的注入是在Bean实例化之后的属性赋值阶段解决的,单例Bean只实例化一次、属性负责也只发生一次,所以,依赖对象也只能被注入一次,虽然属性是原型Bean,由于只能被注入一次所以也就只能是同一个对象了。

其实建议最好不要在单例Bean中引用原型Bean,如果非要这么做的话,Spring也提供了一种解决方法:Method Injection,可以采用xml配置或注解@Lookup实现,个人感觉应用很少,暂时忽略。

上一篇 Spring FrameWork从入门到NB - 自动装配

你可能感兴趣的:(javaspring)