AnnotationConfigApplicationContext.getBean返回另一个bean,Spring [英] AnnotationConfigApplicationContext.getBean returns a different bean, Spring

查看:316
本文介绍了AnnotationConfigApplicationContext.getBean返回另一个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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆