0

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
3
  • Did you take a look at @Configuration component in spring? Seems like this would be the thing you need, to put all bean initialization code there. Commented Apr 22, 2017 at 13:06
  • Updated the question with your suggestion, thank you Commented Apr 22, 2017 at 13:32
  • 2
    Bean is singleton by default even if seems that "return new" creates multiple objects. Use @Scope(BeanDefinition.SCOPE_PROTOTYPE) Commented Apr 22, 2017 at 16:23

1 Answer 1

1

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();
            }
        });

    }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.