How to Produce prototype objects from singleton? (

2019-04-22 05:35发布

I'm relatively new to Spring and I've got myself dug in a hole. I'm trying to model motor cars. Each model has it's own builder object, and I have a BuilderFactory that returns the correct builder based upon user selection from a web-app.

So I'm looking for suggestions on how to approach this problem where I need to create a number of individual vehicles, but I don't know what type of vehicle I'm going to need until run-time, and each vehicle needs to be unique to the user.

What I've got at the moment is shown below. The problem I have at the moment is that because the individual builders are singletons so are the individual vehicles. I need them to be prototypes. I know it all looks pretty horrible so I'm sure there must be a better way of doing this.

The top level from the web-app looks like;

Vehicle vehicle = vehicleBuilderFactory.getBuilder(platform).build();

My vehicleBuilderFactory looks like this;

@Service
public class VehicleBuilderFactory {

@Autowired
Discovery3Builder discovery3Builder;
@Autowired
Discovery4Builder discovery4Builder;

    // Lots of @Autowired statements here. 

@Autowired
FreeLander2010Builder freeLander2010Builder;



public VehicleBuilder getBuilder(Platform platform) {

    switch (platform.getId()) {

    case 1: return discovery3Builder;
    case 2: return discovery4Builder;

            // Lots of case statements here

            case 44: return freeLander2010Builder;
    default: return null;
    }
}

}

which itself looks pretty horrible. Each individual builder looks like;

@Service
public class DefenderBuilder implements VehicleBuilder {

@Autowired
Defender defender;

// Loads of Defender specific setters ommitted  
@Override
public Vehicle build() {
    return defender;
}

}

and finally the individual vehicle

@Service
@Scope("prototype")
public class Defender extends Vehicle {

}

The main problem now, is that because the builders are singletons, so are the vehicles, and I need them to be prototypes, because User A's Defender is different to user B's Defender.

3条回答
Emotional °昔
2楼-- · 2019-04-22 06:15

Without reading too much into the detail you say you want to produce Prototype beans from a singleton possibly with a look up in the IoC container.

Section 3.4.6.1 Lookup method injection of the Spring documentation describes how this can be done without losing the Inversion of Control i.e. without your beans knowing about the bean store.

I have made use of the ServiceLocatorFactoryBean to solve a similar problem before. The class level Javadoc is excellent and contains some clear examples.

查看更多
在下西门庆
3楼-- · 2019-04-22 06:22

Two things: 1) You can use proxy in order to hold narrower scope from wider scope(e.g prototype from singleton) All you need is to define the prototype component with the relevant scope and proxyMode You can read about scoped proxy here.

2) Another thing that I have noticed is that you plan to use multiple autowired annotation. note that you can use autowire on a list of interface and it will autowire all components that implements this interface as discussed here.

Moreover you can add a platform id to the VehicleBuilder interface and then generate a map in the constructor e.g:

Map<Integer, VehicleBuilder> vehicleBuilders;
@Autowired
public VehicleBuilderFactory(List<VehicleBuilder> vehicleBuilders) {
   this.vehicleBuilders = vehicleBuilders.stream()
        .collect(Collectors(x -> x.getPlatformId(), x -> x));
}

in that way you can avoid the switch case.

查看更多
唯我独甜
4楼-- · 2019-04-22 06:32

You can use Spring's ObjectFactory to have it service up prototype scoped beans from a singleton scoped bean. The usage is pretty straightforward:

@Component
class DefenderBuilder implement VechicleBuilder {

  @Autowired
  ObjectFactory<Defender> defenderFactory;

  Defender build() {
     return defenderFactory.getObject()
  }
}

@Component
@Scope("prototype")
class Defender {

}

This returns a new Defender on each call to defenderFactory.getObject()

查看更多
登录 后发表回答