当前位置: 首页 > 后端技术 > Java

SpringFramework的单例bean

时间:2023-04-02 02:00:32 Java

Spring的Ioc容器在创建bean时,如果没有特别指定(无论是通过xml配置文件还是注解),Spring默认会创建一个单例bean。对应单例bean,Spring也可以提供原型bean,需要用户在配置bean时指定。单例bean和原型bean的主要区别Spring的单例bean是指在Spring容器中只保存一个bean的实例。用户在应用程序中获取bean的时候,即使是在不同的模块或者不同的线程中,获取到的其实是同一个bean。singletonbean在容器初始化时创建并初始化,存储在容器中,应用程序使用时直接从容器中获取。原型bean用于单例bean。初始化容器时不会初始化原型bean。只有当应用程序从容器中获取bean时,它才会被创建、初始化并返回给应用程序。即使应用程序在同一个模块、同一段代码中两次获取原型bean,返回的也是不同的bean实例。为什么会有单例bean或者为什么Spring默认提供单例bean而不是原型bean。Spring框架其实考虑的是性能问题。初始化容器时,将创建并初始化bean。应用程序在使用时直接从容器中获取,这样会节省创建和初始化bean实例的开销,大大提高应用程序的性能。那你为什么需要原型bean?其实,凡事有利有弊。单例bean在提升性能的同时,也存在线程安全的隐患。特别是,您的业务bean中有成员变量。由于单例bean在整个容器生命周期中只有一个bean实例,这个成员变量在多线程环境下非常容易造成安全问题。在这种情况下,除了线程安全保护bean成员变量的选择之外,Spring还为你提供了另一个选择,prototypebean。从上面prototypebean和singletonbean的区别,很容易知道prototypebean不存在上面的线程安全问题。单例bean是不是意味着这个类在容器中只能有一个实例对象?这个问题的答案大家都知道:不一定,可以有多个。原因是大家都知道Spring的单例和设计模式中的单例模式其实不是一回事。Spring的单例并不是单例模式的实现。如果进一步解释,你会看到很多人这样说:因为Spring可以有多个容器,而同一个类的singletonbean注册在不同的容器中,那么在多个容器中就会有这个类的多个实例。实例。我个人认为这个解释是不合格的,因为这个解释并没有真正阐明Spring容器中单例bean的含义。我们回想一下,同一个类其实可以在Spring容器中注册多个实例对象。否则,就不会有按类型/按名称这样的东西。对于同一个类,我们可以使用不同的名字,将这个类的多个对象注入到Spring容器中。然后我们可以通过不同的方式获取你想要的实例bean:通过@Qualifier注解通过@primary注解通过名称(beanid)获取单例bean例如:我们创建一个userService类:@ComponentpublicclassuserService{//默认是单例bean,会在容器初始化的时候创建并初始化publicuserService(){System.out.println("userService构造...");}然后,在配置类中定义多个userService实例:}}编写启动类:@Slf4jpublicclasstestApp{publicstaticvoidmain(String[]args){log.info("programisrunningContextwillbecreated");ApplicationContextctx=newAnnotationConfigApplicationContext(commonConfig.class);System.out.println("上下文创建于...");userServiceu3=ctx.getBean("userService3",userService.class);userServiceu4=ctx.getBean("userService4",userService.class);System.out.println("u3here:"+u3);System.out.println("这里是u4:"+u4);userServiceu5=ctx.getBean("userService3",userService.class);userServiceu6=ctx.getBean("userService4",userService.class);System.out.println("u5here:"+u5);System.out.println("u6here:"+u6);运行启动类,在控制台查看运行结果:u3here:org.example.service。userService@35d019a3u4这里:org.example.service.userService@18078befu5这里:org.example.service.userService@35d019a3u6这里:org.example.service.userService@18078bef说明:u3/u5是从Spring容器获取的名称程序运行的不同地方userService3的userService对象,从运行结果来看,得到的是同一个对象,第一步的结果可以看出,由于userService3是单例对象,所以在整个声明周期的不同位置得到弹簧容器。同一个对象。尝试在不同的线程中获取userService3,仍然是同一个对象。以上实验可以看出,在Spring容器中创建并存储的名为userService3的userService对象是单例的,并且是唯一的。获取名为userService3和userService4的userService对象,会得到不同的对象。说明Spring容器可以创建并保存多个userService类型和单例作用域的对象。结论:可以在Spring容器中创建多个同种类型的不同名称的单例对象并存储。这是Spring的单例bean和单例模式最本质的区别。原型bean比如将上面的userService定义为原型bean:@Component@Scope("prototype")publicclassuserService{//通过注解定义为原型bean,在容器初始化的时候,userService的实例不会被创建了publicuserService(){System.out.println("userServiceconstructed...");}重新运行上面的案例,可以发现多次获取到的userService3是不同的bean实例对象。多线程的情况后面会补充。