Reputation: 1119
I am currently working on an spring boot application that wires some beans together in the following way (heavily simplified example):
@Component
@Order(0)
public class PlayingFieldByBeans implements CommandLineRunner {
@Override
public void run(String... arg0) throws Exception {
List<String> names = new ArrayList<>();
names.add("Alex");
names.add("Benedict");
names.add("Chloe");
System.out.println("Printing from lazy beans variant: ");
names.forEach(n -> {
System.out.println(player(n));
});
}
@Bean
@Lazy
public Player player(String name) {
return new Player(name, shoes());
}
@Bean
@Lazy
private Shoes shoes() {
return new Shoes("Adidas");
}
}
The actual beans however, require alot more configuration and setting than is shown here and it takes quite alot of lines of code in the PlayingFieldByBeans class when using the inner Lazy Bean methodology. So I created a different way of wiring it together using Component
annotation:
@Component
@Order(1)
public class PlayingFieldByComponents implements CommandLineRunner {
@Autowired
private PlayerComponent playerComponent;
@Override
public void run(String... arg0) throws Exception {
List<String> names = new ArrayList<>();
names.add("Alex");
names.add("Benedict");
names.add("Chloe");
System.out.println("Printing from component variant: ");
names.forEach(n -> {
System.out.println(playerComponent.player(n));
});
}
}
The PlayerComponent class looks like this:
@Component
public class PlayerComponent {
@Autowired
private ShoesComponent shoesComponent;
public Player player(String name) {
return new Player(name, shoesComponent.shoes());
}
}
The ShoesComponent is very similar to the PlayerComponent class.
For maintainablity and TDD purposes I am not sure what is the most proper way to use the spring framework here.
Question
Given the Player and Shoes beans require more then just one line of initialization (multiple settings, multiple dependencies on other beans etc), what is the best way to design and wire them?
Edit - based on suggestion
Added a configuration class to bundle the beans:
@Configuration
public class BeanConfiguration {
@Bean
@Lazy
public Player player(String name) {
return new Player(name, shoes());
}
@Bean
@Lazy
public Shoes shoes() {
return new Shoes("Adidas");
}
}
And the matching executing class:
@Component
@Order(2)
public class PlayingFieldByConfiguration implements CommandLineRunner {
@Autowired
private BeanConfiguration beanConfiguration;
@Override
public void run(String... arg0) throws Exception {
List<String> names = new ArrayList<>();
names.add("Alex");
names.add("Benedict");
names.add("Chloe");
System.out.println("Printing from component variant: ");
names.forEach(n -> {
System.out.println(beanConfiguration.player(n));
});
}
}
Re uses the same first bean, so it doesn't seem to create a new one
Printing from component variant:
Player name: Alex has shoes of brand: Adidas
Player name: Alex has shoes of brand: Adidas
Player name: Alex has shoes of brand: Adidas
Upvotes: 0
Views: 168
Reputation: 161
One solution would be to change scope of Player bean (and Shoes later on if we want to create different brands) as mentioned by Andriy Slobodyanyk
@Configuration
public class BeanConfiguration {
@Bean
@Lazy
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public Player player(String name) {
return new Player(name, shoes());
}
@Bean
@Lazy
public Shoes shoes() {
return new Shoes("Adidas");
}
}
If above would not be sufficient (since you mentioned real case scenario is more compilcated) another option is to use FactoryBean
public class PlayerFactoryBean implements FactoryBean<Player> {
private String name;
private Shoes shoes;
public void setName(String name) {
this.name = name;
}
public void setShoes(Shoes shoes) {
this.shoes = shoes;
}
@Override
public Player getObject() throws Exception {
//initialization logic goes here
System.out.println("Creating bean using factory");
return new Player(name, shoes);
}
@Override
public Class<Player> getObjectType() {
return Player.class;
}
@Override
public boolean isSingleton() {
return false;
}
}
@Configuration
public class BeanConfiguration {
@Bean
@Lazy
public Shoes shoes() {
return new Shoes("Adidas");
}
@Bean
public PlayerFactoryBean playerFactoryBean(){
PlayerFactoryBean pfb = new PlayerFactoryBean();
pfb.setShoes(shoes());
return pfb;
}
}
@Component
@Order(2)
public class PlayingFieldByConfiguration implements CommandLineRunner {
@Autowired
private PlayerFactoryBean factoryBean;
@Override
public void run(String... arg0) throws Exception {
List<String> names = new ArrayList<>();
names.add("Alex");
names.add("Benedict");
names.add("Chloe");
System.out.println("Printing from component variant: ");
names.forEach(n -> {
try {
factoryBean.setName(n);
System.out.println(factoryBean.getObject());
} catch (Exception e) {
e.printStackTrace();
}
});
}
}
Upvotes: 1