Suppose I've a class Fruit
and it's two subclasses - Apple
and Grape
:
class Fruit {
public void grind() { }
}
class Apple extends Fruit { }
class Grape extends Fruit { }
In spring properties file, I've a property that decides which bean to register at startup. At a time, I'll only have either Apple
or Grape
instance registered as a bean. The property is:
# This can be either apple or grape
app.fruit = apple
In the Java configuration file, I'm binding a String
attribute using @Value
with this property, and based on that, I'll create appropriate instance. I'm trying to use factory pattern here. So, I've a FruitFactory
like this:
class FruitFactory {
private Map<String, Fruit> map = new HashMap<String, Fruit>();
public FruitFactory() {
map.put("apple", new Apple());
map.put("grape", new Grape());
}
public Fruit getFruit(String fruit) {
return map.get(fruit);
}
}
And here's my spring configuration class:
class SpringConfig {
@Value("${app.fruit}")
private String fruitType;
@Bean
public FruitFactory fruitFactory() {
return new FruitFactory();
}
@Bean
public Fruit getFruit() {
return fruitFactory().getFruit(fruitType);
}
}
So, here're my few questions:
Will the instances stored in the map inside the factory be spring managed bean? Is there any issue with the implementation? I've tried it, and it is working fine, and I'm confused whether the instances are really spring managed.
I was trying to implement it in a better way, so that when a new fruit comes, I don't have to modify my factory. On way is to provide a
register()
method in factory and let all theFruit
subclasses invoke it. But the issue is when and how the subclasses will be loaded? I'll not be using the classes, not before putting their instances into the map. Can anyone suggest a better way?
Edit:
As suggested in comment and answer, I've tried using @Profile
instead of factory pattern. But I'm facing some issues in that. Here's what I've:
@Configuration
@Profile("apple")
class AppleProfile {
@Bean
public Fruit getApple() {
return new Apple();
}
}
@Configuration
@Profile("grape")
class GrapeProfile {
@Bean
public Fruit getGrape() {
return new Grape();
}
}
And in a ServletListener
, I've set the active profile:
class MyServletListener implements ServletContextListener {
@Value("${app.fruit}")
private String fruitType;
public void contextInitialized(ServletContextEvent contextEvent) {
// Get Spring Context
WebApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(contextEvent
.getServletContext());
context.getAutowireCapableBeanFactory().autowireBean(this);
ConfigurableEnvironment configEnvironment = (ConfigurableEnvironment) context.getEnvironment();
logger.debug("0;Setting Active Profile: " + cacheRetrievalMode);
configEnvironment.setActiveProfiles(cacheRetrievalMode);
}
}
This is properly setting the active profile, which I can see. The only issue is, the listener is declared before the ContextLoaderListener
, and by the time this is executed, the beans are already been created. Is there any alternative?