0

I have an application , and now I am trying to use Spring to refactor it, and I have problem when creating object using new, but I don't know how to solve it.

Here is the realtionship:

I have a Controller, need a CommandService instance, and the CommandService need a RoomService to create AbstractRoom instances to put into RoomService instance's hashmap.

I have two kinds of AbstractRoom called RoomA, RoomB, and they extend from AbstractRoom, and AbstractRoom needs GameService instance.

I will pass a param from commandService to roomService so that the roomservice can create a right room instance.

The problem now, is that I use roomservice.createRoom to create a room which uses new to do that. So Spring can not inject GameService to my Abstract Room thus I have a null gameService.

But CommandService will get some input from user and to delegate to RoomService to create a room for it, so I don't know which Room Instance will be created until the user input something:

CommandService.java:

    private String handleCreateRoom(String userID, int playerCount,
        Mode roomMode) {
        ...
        AbstractRoom theNewRoom=roomService.createRoom(userID, playerCount, roomMode);
        ...        
        }

Here is how I createRoom from RoomService.java:

public AbstractRoom createRoom(String userID,int playerCount,Mode roomMode){
    AbstractRoom room = newRoom(roomMode);// create a room based on the roomMode
    room.initRoom(userID, playerCount);// init the room
    userToRoom.put(userID, room.getRoomNum());//some context 
    return room;
}


public AbstractRoom newRoom(AbstractRoom.Mode roomMode) {
    Integer randomNumObject;
    AbstractRoom newRoom;
    .....
    if(roomMode.equals(Mode.AUTO_JUDGE)||roomMode.equals(Mode.PLAYER_JUDGE)){//wodi room

           newRoom=new RoomA(randomNumObject,roomMode);//RoomA
    }
    ....
    else{//RoomB
        newRoom=new RoomB(randomNumObject);
    }

    roomMap.put(randomNumObject, newRoom);//some context 
    return newRoom;
}

Here is my AbstractRoom.java

public abstract class AbstractRoom {

        protected Mode roomMode;    
        @Autowired
        protected GameService gameService;
        .....
}

And my configuration is :

@Configuration
@EnableAspectJAutoProxy
public class Application {
    @Bean
    public CommandService commandService(){
        return new CommandService();//singleton
    }
    @Bean
    public RoomService roomService(){
        return new RoomService();//singleton
    }       
    @Bean
    public GameService gameService(){
        return new GameService();//singleton
    }
JaskeyLam
  • 15,405
  • 21
  • 114
  • 149
  • I wouldn't make a Room dependant on a service. That looks wrong. If you really need that, then pass the service explicitely in the room constructor, or make Room a prototype bean and use the Spring application context to get an instance. – JB Nizet Nov 02 '14 at 10:20
  • "make Room a prototype bean and use the Spring application context to get an instance." I guess this is what I want for now, but I am new to Spring would you please show me how to do that in the answer? Thank you! – JaskeyLam Nov 02 '14 at 10:24
  • http://stackoverflow.com/questions/22155832/spring-java-config-how-do-you-create-a-prototype-scoped-bean-with-runtime-argu – JB Nizet Nov 02 '14 at 10:31
  • @JBNizet, thank you! now I use new AnnotationConfigApplicationContext(Application.class).getBean(RoomService.class), when declaring a bean of AbstratRoom, But I have another problem that my service is not singlton now! http://stackoverflow.com/questions/26698962/annotationconfigapplicationcontext-getbean-returns-a-different-bean-spirng – JaskeyLam Nov 02 '14 at 12:05
  • You create an application context once, and only once. Autowire it where you need it, just like in the answer I linked to. – JB Nizet Nov 02 '14 at 12:35
  • @JBNizet, yes , I tried, reusing the same ctx, and the same, not the same id.And I find the constructor is called several times when I run my app but I don't know if it is a clue. Would you please go to that question and update there? – JaskeyLam Nov 02 '14 at 12:38
  • Post what you're tried. – JB Nizet Nov 02 '14 at 12:41
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/64118/discussion-between-jaskey-and-jb-nizet). – JaskeyLam Nov 02 '14 at 12:41

1 Answers1

0

Eventually, I solve this problem with letting AbstractRoom as a bean with scope = prototype, and return the instance from roomservice bean.

@Configuration
@EnableAspectJAutoProxy
public class Application {      
    @Bean
    public CommandService commandService(){
        return new CommandService();
    }

    @Bean
    public RoomService roomService(){
        return new RoomService();
    }

    @Bean
    public GameService gameService(){
        return new GameService();
    }

    @Bean
    @Scope("prototype")
    public AbstractRoom room(AbstractRoom.Mode roomMode){
        RoomService roomService = roomService();
        return roomService.newRoom(roomMode);
    }

And in RoomService.java , inject an ApplicationContext, and get Room from the container.

public class RoomService {

    @Autowired
    private ApplicationContext ctx;

    public AbstractRoom createRoom(String userID,int playerCount,Mode roomMode){
        AbstractRoom room = (AbstractRoom)ctx.getBean(AbstractRoom.class,roomMode);
    }
}
JaskeyLam
  • 15,405
  • 21
  • 114
  • 149