AnnotationConfigApplicationContext.getBean()方法返回一个不同的bean,Spring
AnnotationConfigApplicationContext.getBean()方法返回一个不同的bean,Spring
我有一个问题,我有一个ClassA需要注入RoomService
,我发现在ClassA中,roomService的id是相同的,这是正常的。但是由于某些原因,我需要roomservice根据一些输入参数为我创建room实例,所以我使用以下配置来实现:
@Configuration @EnableAspectJAutoProxy public class Application { private static ApplicationContext ctx = new AnnotationConfigApplicationContext(Application.class); public static ApplicationContext getApplicationContext(){ return ctx; } @Bean public RoomService roomService(){ return new RoomService();//单例 } @Bean @Scope("prototype") public AbstractRoom room(AbstractRoom.Mode roomMode){ RoomService roomService = (RoomService) ctx.getBean(RoomService.class); LogUtil.debug("--------from application:" +roomService.id1);//在这里,我发现每次id都不同 return roomService.newRoom(roomMode); } }
问题是我需要RoomService
是单例的,但是我发现在Application.java中,ctx.getBean(roomService)总是返回一个不同的bean,其id也不同。难道Spring不应该重用同一个bean吗?为什么会这样?
这是我在RoomService.java中创建房间的方法:
public AbstractRoom createRoom(String userID,int playerCount,Mode roomMode){ ApplicationContext ctx =Application.getApplicationContext(); AbstractRoom room = (AbstractRoom)ctx.getBean(AbstractRoom.class,roomMode); }
更新:
我尝试重用相同的ctx,但是没有成功。一个提示是当我运行tomcat启动时,我发现我的RoomService()
构造函数被多次调用(我在其中设置了断点)。
这是我的web.xml文件:
wodinow contextConfigLocation /WEB-INF/dispatcher-servlet.xml org.springframework.web.context.ContextLoaderListener dispatcher org.springframework.web.servlet.DispatcherServlet 1 dispatcher /
请帮忙!
问题出现的原因:每次调用AnnotationConfigApplicationContext.getBean()方法时都创建了一个新的ApplicationContext实例,但是单例bean只在单个ApplicationContext实例中保证是单例的。
解决方法:如果想要bean是单例的,必须每次都使用相同的ApplicationContext实例来获取它。
文章内容如下:
每次调用AnnotationConfigApplicationContext.getBean()方法时都创建了一个新的ApplicationContext实例,但是单例bean只在单个ApplicationContext实例中保证是单例的。所以如果想要bean是单例的,必须每次都使用相同的ApplicationContext实例来获取它。
根据Spring文档的描述,单例bean的定义如下:
singleton: (Default) 将一个bean定义范围限制为Spring IoC容器中的单个对象实例。
更新1:
可以在room()方法中直接调用roomService()方法来获取room service,而不需要创建ApplicationContext,因为roomService()方法已经被标记为单例作用域。
更新2:
根据更新后的问题,代码中存在一些问题:
1. 不要在配置类中创建ApplicationContext。当在Tomcat中启动Spring应用程序时,Spring会自动为您创建应用程序上下文。您只需要告诉Spring应该注册哪些配置类。
2. 从配置类中删除room()方法的bean定义。创建房间的方法应该在RoomService中。例如,如果需要在Spring MVC控制器中创建一个新的房间,您可以注入RoomService并在其上调用createRoom方法。注入的service将是单例的。
尝试根据这些建议重新编写代码,应该可以解决问题。
在我创建的简单项目中测试了这个方法,它是可行的。但是我没有在Tomcat中运行它。另外,我刚刚查看了您更新的问题。如果您在Tomcat中运行,请不要在配置类中像那样定义静态应用程序上下文。应用程序上下文将自动创建。您如何启动Spring应用程序并注册配置类?是使用web.xml还是使用Java配置(Web应用程序初始化程序)?如果可能,请共享该代码。这可能会有所帮助。
我通过根据第二个答案在roomService中自动注入一个applicationcontext来解决了这个问题,具体方法请参考这个链接:stackoverflow.com/questions/129207/…。请您更新您的答案,并解释为什么我必须注入applicationcontext,而使用Application.java的getInstance方法却不起作用?之后我会接受您的答案。非常感谢!
在更新之前,我最后一个问题。您在room service中注入了applicationcontext吗?
是的,我在roomService中注入了一个applicationcontext,现在它可以正常工作了。但是对于我的当前关系,我需要room的new
,因为我需要Spring为我创建room bean,因为room需要注入其他东西,请参考这个问题:stackoverflow.com/questions/26698207/…。所以,请您解释一下什么时候应该使用new ApplicationContext,什么时候不应该使用?因为一开始您似乎同意使用new
是可以的。非常感谢!
起初我以为您是在一个独立的应用程序中使用它,因此创建新的上下文应该是可以的。但是在您更新之后,我发现您是在Tomcat中运行应用程序,并且Spring上下文是由Spring自动创建的,所以在这种情况下,不应该像您的示例中那样手动创建上下文。可能之前没有按照您的预期工作是因为您有多个上下文。现在,当您在RoomService中自动注入应用程序上下文时,它引用了与RoomService bean本身所在的相同上下文,因此您的bean可以正确地作为单例使用。
如果这不是Spring控制的bean,但是我需要访问ApplicationContext,我该怎么办?