AnnotationConfigApplicationContext.getBean返回另一个bean,Spring [英] AnnotationConfigApplicationContext.getBean returns a different bean, Spring
问题描述
我有一个问题,我有一个ClassA需要注入RoomService
,并且在ClassA中,roomService的ID是相同的,效果很好.
出于某种原因,我需要roomservice为我基于一些输入参数创建房间实例,因此我使用下面的config来实现此目的:
@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();//Singleton
}
@Bean
@Scope("prototype")
public AbstractRoom room(AbstractRoom.Mode roomMode){
RoomService roomService = (RoomService) ctx.getBean(RoomService.class);
LogUtil.debug("--------from application:" +roomService.id1);//here, I find the id is different every time
return roomService.newRoom(roomMode);
}
}
问题是我需要RoomService
成为单例,但是我发现在Application.java中,ctx.getBean(roomService)始终返回具有不同id的另一个bean. 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
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>wodinow</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
请帮助!
每次检索RoomService
时,您都在创建一个新的ApplicationContext
实例.但是,单例bean仅保证在ApplicationContext
的单个实例中是单例.
因此,如果要使bean为单例,则每次检索它时都必须使用相同的ApplicationContext
实例.
摘自Spring文档:
singleton :(默认)将单个bean定义的作用域限定为 single 每个Spring IoC容器的对象实例.
更新1
您只需在room()
方法中调用roomService()
即可获得客房服务,而无需创建应用程序上下文,Spring将确保它们是同一实例,因为它被标记为具有隐式单例作用域的@Bean
./p>
更新2
根据更新后的问题,以下是您的代码中的一般问题:
1..不要在配置类中创建ApplicationContext
.当您在 Tomcat 中启动Spring应用程序时,Spring将为您创建应用程序上下文.您只需要告诉Spring它应该注册哪些配置类即可.
2..从配置类中删除room()
bean定义.创建房间的方法应该在RoomService
中.因此,例如,如果您需要在Spring MVC控制器中创建一个新房间,则可以注入RoomService
并在其上调用createRoom
方法.注入的服务将是单例.示例:
@Controller
@RequestMapping("/rooms")
public class RoomController {
@Autowired
private RoomService roomService;
@RequestMapping(value="/create", method=POST)
public String createRoom() {
roomService.createRoom(/* Parameters for room creation */);
return "redirect:/somelocation";
}
}
尝试根据这些建议对代码进行重做,它应该可以工作.
I have a problem that I have a ClassA needs RoomService
to be injected, and it works fine that I find in ClassA, the roomService's id is the same.
While for some reason, I need roomservice to create room instance based on some input param for me, so I use below config to achieve this:
@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();//Singleton
}
@Bean
@Scope("prototype")
public AbstractRoom room(AbstractRoom.Mode roomMode){
RoomService roomService = (RoomService) ctx.getBean(RoomService.class);
LogUtil.debug("--------from application:" +roomService.id1);//here, I find the id is different every time
return roomService.newRoom(roomMode);
}
}
The problem is that I need RoomService
to be singleton, but I find that in the Application.java , the ctx.getBean(roomService) always returns a different bean which has different id. Isn't Spring should reuse the same bean? Why is that?
Here is how I create a room in RoomService.java
public AbstractRoom createRoom(String userID,int playerCount,Mode roomMode){
ApplicationContext ctx =Application.getApplicationContext();
AbstractRoom room = (AbstractRoom)ctx.getBean(AbstractRoom.class,roomMode);
}
Update:
I tried reusing the same ctx and it does not work. One hint is that I find my constructor of RoomService()
is called several times(I put a break point in it.) when I run tomcat to start it
Here is my web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>wodinow</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
Please help!
Each time you retrieve your RoomService
you are creating a new ApplicationContext
instance. But singleton beans are only guaranteed to be singleton within a single instance of ApplicationContext
.
So if you want the bean to be singleton you must use the same ApplicationContext
instance each time you retrieve it.
From Spring documentation:
singleton: (Default) Scopes a single bean definition to a single object instance per Spring IoC container.
Update 1
You can just call roomService()
in your room()
method to get the room service without creating application context and Spring will ensure that they are the same instance since it is marked as @Bean
which has implicit singleton scope.
Update 2
Based on the updated question here are couple of issues with your code in general:
1. Do not create ApplicationContext
in your configuration class. When you start your Spring application in Tomcat, application context is created for you by Spring. You just need to tell Spring which configuration classes it should register.
2. Remove the room()
bean definition from your configuration class. Method for creating a room should be in RoomService
. So for example if you needed to create a new room in Spring MVC controller you would inject the RoomService
and call createRoom
method on it. The injected service would be singleton. Example:
@Controller
@RequestMapping("/rooms")
public class RoomController {
@Autowired
private RoomService roomService;
@RequestMapping(value="/create", method=POST)
public String createRoom() {
roomService.createRoom(/* Parameters for room creation */);
return "redirect:/somelocation";
}
}
Try to rework your code based on these suggestions and it should work.
这篇关于AnnotationConfigApplicationContext.getBean返回另一个bean,Spring的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!