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:
- 通过@Qualifier注解
- 通过@primary注解
- 通过名字(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
阐明:
- u3/u5是在程序运行的不同地位向Spring容器获取的名字为userService3的userService对象,从运行后果看,获取到的是同一个对象。
- 第一步的运行后果阐明,因为userService3是单例对象,所以在Spring容器的整个申明周期中的不同地位获取到的是同一个对象。
- 尝试在不同的线程中获取userService3,依然是同一个对象。
- 以上试验能够阐明,Spring容器中创立并存储的名字为userService3的userService对象是单例的、只有一个。
- 获取名字为userService3、userService4的userService对象,失去的是不同的对象。
- 阐明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实例对象。
多线程的案例当前补充。