Spring的Ioc容器创立bean的时候,如果不做非凡指定(不论是通过xml配置文件、或者是注解形式),则Spring默认会创立单例bean。

与单例bean对应的,Spring还能够提供原型bean,原型bean须要用户在配置bean的时候非凡指定。

单例bean和原型bean的次要区别

Spring的单例bean指的是,Spring容器中只保留该bean的一个实例,用户在利用中获取bean的时候,即便在不同的模块、或不同的线程中,获取到的其实是同一个bean。

单例bean在容器初始化的时候就会创立并实现初始化,并存储在容器中,利用在应用的时候间接从容器中获取。

原型bean是针对单例bean而言的,原型bean在容器初始化的时候并不会初始化,只有在利用向容器获取bean的时候,才会创立、初始化并返回给利用。

利用即便是在同一模块中、同一段代码中,两次获取原型bean,返回的也是不同的bean实例。

为什么要有单例bean

或者说,Spring为什么默认提供的是单例bean,而不是原型bean。Spring框架思考的其实是性能问题,容器初始化的时候就创立并初始化好bean,利用应用的时候间接从容器中获取,就会节约bean实例创立及初始化过程中的开销,极大进步利用的性能。

那为什么还会须要原型bean

其实,任何事件都会有正反两面,单例bean在进步性能的同时,也存在线程平安的隐患。

尤其是你的业务bean中存在成员变量,因为单例bean在整个容器生命周期过程中只存在一个bean实例,该成员变量非常容易导致多线程环境下的平安问题。

这种状况下,除了能够抉择对bean成员变量进行线程平安的爱护之外,Spring还给你提供了另外一种抉择就是原型bean。

由以上原型bean与单例bean的区别局部,非常容易的能晓得,原型bean不存在以上的线程平安问题。

单例bean就示意容器中只能有该类的一个实例对象吗?

这个问题的答案大家都晓得:不肯定,能够有多个。

起因是大家都晓得Spring的单例和设计模式中的单例模式理论不是一回事,Spring的单例并不是单例模式的实现。

进一步解释,就会看到有很多人这样说:因为Spring能够有多个容器,同一个类的单例bean注册到不同的容器中,那么,在多个容器中就会存在该类的多个实例。

集体感觉这个解释不合格,因为这样的解释并没有真正说分明Spring容器中单例bean的含意。

咱们回顾一下,同一个类,其实是能够在Spring容器中注册多个实例对象的。否则就不会存在by type/by name这样的说法了。

同一个类,咱们能够用不同的名字,注入该类的多个对象到Spring容器中。

而后咱们能够通过不同的形式,获取到你想要的实例bean:

  1. 通过@Qualifier注解
  2. 通过@primary注解
  3. 通过名字(bean id)获取

单例bean举例

比方:咱们创立一个userService类:

@Componentpublic class userService{    //默认是单例bean,在容器初始化的时候就会被创立、初始化    public userService(){        System.out.println("userService constructed。。。");    } 

而后,在配置类中定义多个userService的实例:

@Configurationpublic class commonConfig  {    @Bean    public userService userService3(){        return new userService();    }    @Bean    public userService userService4(){        return new userService();    }}

编写启动类:

@Slf4jpublic class testApp {    public static void main( String[] args )    {        log.info("program is running Context will be created");        ApplicationContext ctx=new AnnotationConfigApplicationContext(commonConfig.class);        System.out.println("Context created over。。。");    userService u3=ctx.getBean("userService3",userService.class);        userService u4=ctx.getBean("userService4",userService.class);        System.out.println("u3 here:" + u3);        System.out.println("u4 here:" + u4);        userService u5=ctx.getBean("userService3",userService.class);        userService u6=ctx.getBean("userService4",userService.class);        System.out.println("u5 here:" + u5);        System.out.println("u6 here:" + u6);

运行启动类,控制台查看运行后果:

u3 here:org.example.service.userService@35d019a3u4 here:org.example.service.userService@18078befu5 here:org.example.service.userService@35d019a3u6 here:org.example.service.userService@18078bef

阐明:

  1. u3/u5是在程序运行的不同地位向Spring容器获取的名字为userService3的userService对象,从运行后果看,获取到的是同一个对象。
  2. 第一步的运行后果阐明,因为userService3是单例对象,所以在Spring容器的整个申明周期中的不同地位获取到的是同一个对象。
  3. 尝试在不同的线程中获取userService3,依然是同一个对象。
  4. 以上试验能够阐明,Spring容器中创立并存储的名字为userService3的userService对象是单例的、只有一个。
  5. 获取名字为userService3、userService4的userService对象,失去的是不同的对象。
  6. 阐明Spring容器能够创立并保留类型为userService、作用域为单例的多个对象。

论断:
Spring容器中能够创立并存储多个名称不同的某一类型的单例对象,这是Spring的单例bean与单例模式的最实质的区别

原型bean举例

将以上userService定义为原型bean:

@Component@Scope("prototype")public class userService{    //通过注解定义为原型bean,容器初始化的时候,不会创立userService的实例    public userService(){        System.out.println("userService constructed。。。");    } 

重跑以上案例,能够发现,屡次获取的userService3为不同的bean实例对象。

多线程的案例当前补充。